Download do Dataset "The Movies Dataset" do Kaggle¶

Link para o Dataset no Kaggle

Para baixar e utilizar o dataset diretamente no seu notebook, siga os passos abaixo:

1. Instalar a Biblioteca Kaggle¶

Primeiro, certifique-se de que a biblioteca kaggle está instalada. Se não estiver, você pode instalá-la com o seguinte comando:

!pip install kaggle
In [1]:
# !pip install kaggle
# !pip install mlxtend

2. Download do Dataset¶

Nesta seção, estamos realizando o download do "The Movies Dataset" do Kaggle diretamente para o nosso ambiente de desenvolvimento.

Baixando o Dataset¶

O comando a seguir utiliza a biblioteca kaggle para fazer o download do dataset diretamente do site do Kaggle:

!kaggle datasets download -d rounakbanik/the-movies-dataset

Após o download, o dataset está compactado em um arquivo ZIP. Para utilizá-lo, precisamos extrair os arquivos contidos nele. O código abaixo realiza essa extração:

import zipfile
import os

with zipfile.ZipFile("the-movies-dataset.zip", "r") as zip_ref:
    zip_ref.extractall("the-movies-dataset")

Análise Exploratória dos Dados do Conjunto "The Movies Dataset"¶

Neste notebook, realizaremos uma análise exploratória completa do conjunto de dados "The Movies Dataset" disponível no Kaggle. Esta análise incluirá o carregamento dos dados, compreensão da sua estrutura, limpeza dos dados e realização de várias análises para obter insights sobre a indústria cinematográfica.

In [2]:
#Fazendo as instalações necessárias
# !pip install wordcloud
# !pip install textblob
# nltk.download('punkt')
# nltk.download('stopwords')
In [3]:
# Importando as bibliotecas necessárias
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import plotly.graph_objects as go
import plotly.io as pio
from wordcloud import WordCloud, STOPWORDS
from mlxtend.frequent_patterns import apriori, association_rules
from tqdm import tqdm
import random
from collections import Counter
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
import re
from wordcloud import WordCloud
from nltk.util import ngrams
import ast
from textblob import TextBlob

pio.renderers.default = "notebook"


# Configurando o estilo dos gráficos
sns.set(style="whitegrid")

Carregando os Dados¶

Vamos começar carregando os arquivos CSV disponíveis no conjunto de dados. Esses arquivos contêm diversas informações relacionadas a filmes, como créditos, links, metadados dos filmes, avaliações, entre outros.

In [4]:
# Carregando os diferentes arquivos CSV
links_small = pd.read_csv('the-movies-dataset/links_small.csv')
movies_metadata = pd.read_csv('the-movies-dataset/movies_metadata.csv')
C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\3125572303.py:3: DtypeWarning:

Columns (10) have mixed types. Specify dtype option on import or set low_memory=False.

Explorando a Estrutura dos Dados¶

Nesta seção, vamos explorar a estrutura de cada um dos arquivos carregados, analisando o número de linhas e colunas, o tipo de dados presente em cada coluna, e identificando valores nulos ou ausentes. Utilizaremos apenas os datasets links_small.csv e movies_metadata.csv, pois são versões menores das informações sobre os filmes.

In [5]:
# Exibindo a estrutura dos dados dos datasets
print("\nEstrutura de Movies Metadata:")
print(movies_metadata.head())
print(movies_metadata.info())

print("\nEstrutura de Links Small:")
print(links_small.head())
print(links_small.info())
Estrutura de Movies Metadata:
   adult                              belongs_to_collection    budget  \
0  False  {'id': 10194, 'name': 'Toy Story Collection', ...  30000000   
1  False                                                NaN  65000000   
2  False  {'id': 119050, 'name': 'Grumpy Old Men Collect...         0   
3  False                                                NaN  16000000   
4  False  {'id': 96871, 'name': 'Father of the Bride Col...         0   

                                              genres  \
0  [{'id': 16, 'name': 'Animation'}, {'id': 35, '...   
1  [{'id': 12, 'name': 'Adventure'}, {'id': 14, '...   
2  [{'id': 10749, 'name': 'Romance'}, {'id': 35, ...   
3  [{'id': 35, 'name': 'Comedy'}, {'id': 18, 'nam...   
4                     [{'id': 35, 'name': 'Comedy'}]   

                               homepage     id    imdb_id original_language  \
0  http://toystory.disney.com/toy-story    862  tt0114709                en   
1                                   NaN   8844  tt0113497                en   
2                                   NaN  15602  tt0113228                en   
3                                   NaN  31357  tt0114885                en   
4                                   NaN  11862  tt0113041                en   

                original_title  \
0                    Toy Story   
1                      Jumanji   
2             Grumpier Old Men   
3            Waiting to Exhale   
4  Father of the Bride Part II   

                                            overview  ... release_date  \
0  Led by Woody, Andy's toys live happily in his ...  ...   1995-10-30   
1  When siblings Judy and Peter discover an encha...  ...   1995-12-15   
2  A family wedding reignites the ancient feud be...  ...   1995-12-22   
3  Cheated on, mistreated and stepped on, the wom...  ...   1995-12-22   
4  Just when George Banks has recovered from his ...  ...   1995-02-10   

       revenue runtime                                   spoken_languages  \
0  373554033.0    81.0           [{'iso_639_1': 'en', 'name': 'English'}]   
1  262797249.0   104.0  [{'iso_639_1': 'en', 'name': 'English'}, {'iso...   
2          0.0   101.0           [{'iso_639_1': 'en', 'name': 'English'}]   
3   81452156.0   127.0           [{'iso_639_1': 'en', 'name': 'English'}]   
4   76578911.0   106.0           [{'iso_639_1': 'en', 'name': 'English'}]   

     status                                            tagline  \
0  Released                                                NaN   
1  Released          Roll the dice and unleash the excitement!   
2  Released  Still Yelling. Still Fighting. Still Ready for...   
3  Released  Friends are the people who let you be yourself...   
4  Released  Just When His World Is Back To Normal... He's ...   

                         title  video vote_average vote_count  
0                    Toy Story  False          7.7     5415.0  
1                      Jumanji  False          6.9     2413.0  
2             Grumpier Old Men  False          6.5       92.0  
3            Waiting to Exhale  False          6.1       34.0  
4  Father of the Bride Part II  False          5.7      173.0  

[5 rows x 24 columns]
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 45466 entries, 0 to 45465
Data columns (total 24 columns):
 #   Column                 Non-Null Count  Dtype  
---  ------                 --------------  -----  
 0   adult                  45466 non-null  object 
 1   belongs_to_collection  4494 non-null   object 
 2   budget                 45466 non-null  object 
 3   genres                 45466 non-null  object 
 4   homepage               7782 non-null   object 
 5   id                     45466 non-null  object 
 6   imdb_id                45449 non-null  object 
 7   original_language      45455 non-null  object 
 8   original_title         45466 non-null  object 
 9   overview               44512 non-null  object 
 10  popularity             45461 non-null  object 
 11  poster_path            45080 non-null  object 
 12  production_companies   45463 non-null  object 
 13  production_countries   45463 non-null  object 
 14  release_date           45379 non-null  object 
 15  revenue                45460 non-null  float64
 16  runtime                45203 non-null  float64
 17  spoken_languages       45460 non-null  object 
 18  status                 45379 non-null  object 
 19  tagline                20412 non-null  object 
 20  title                  45460 non-null  object 
 21  video                  45460 non-null  object 
 22  vote_average           45460 non-null  float64
 23  vote_count             45460 non-null  float64
dtypes: float64(4), object(20)
memory usage: 8.3+ MB
None

Estrutura de Links Small:
   movieId  imdbId   tmdbId
0        1  114709    862.0
1        2  113497   8844.0
2        3  113228  15602.0
3        4  114885  31357.0
4        5  113041  11862.0
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 9125 entries, 0 to 9124
Data columns (total 3 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   movieId  9125 non-null   int64  
 1   imdbId   9125 non-null   int64  
 2   tmdbId   9112 non-null   float64
dtypes: float64(1), int64(2)
memory usage: 214.0 KB
None

Limpeza e Preparação dos Dados¶

Antes de realizar qualquer análise, é importante garantir que os dados estejam limpos e bem preparados. Nesta seção, vamos tratar valores nulos, corrigir tipos de dados, e remover quaisquer inconsistências.

In [6]:
# Removendo linhas onde a coluna 'title' possui valores nulos, pois o título é uma informação essencial
movies_metadata = movies_metadata.dropna(subset=['title'])

# Criando uma nova coluna 'year', que contém apenas o ano extraído da coluna 'release_date'
movies_metadata['year'] = pd.to_datetime(movies_metadata['release_date'], errors='coerce').apply(
    lambda x: str(x).split('-')[0] if pd.notnull(x) else np.nan
)

# Convertendo a coluna 'year' para valores numéricos, substituindo valores inválidos (não numéricos) por NaN
movies_metadata['year'] = pd.to_numeric(movies_metadata['year'], errors='coerce')
movies_metadata = movies_metadata.dropna(subset=['year'])

# Extraindo a decada do filme
movies_metadata['decade'] = (movies_metadata['year'] // 10) * 10

# Removendo possíveis linhas duplicadas, se houver mais de uma linha com o mesmo 'id' (coluna identificadora do filme)
movies_metadata = movies_metadata.drop_duplicates(subset='id', keep='first')

# Convertendo a coluna 'budget' e 'revenue' para valores numéricos, substituindo valores inválidos (não numéricos) por NaN
movies_metadata['budget'] = pd.to_numeric(movies_metadata['budget'], errors='coerce')
movies_metadata['revenue'] = pd.to_numeric(movies_metadata['revenue'], errors='coerce')

# No dataset links_small, filtramos para manter apenas as linhas onde a coluna 'tmdbId' não tem valores nulos e convertemos para inteiros
links_small_id = links_small[links_small['tmdbId'].notnull()]['tmdbId'].astype('int')

# Exibindo a estrutura do dataset movies_metadata e links_small após a limpeza
print("\nEstrutura de Movies Metadata após a limpeza:")
print(movies_metadata.info())

print("\nEstrutura de Links Small após a limpeza:")
print(links_small.info())
Estrutura de Movies Metadata após a limpeza:
<class 'pandas.core.frame.DataFrame'>
Index: 45346 entries, 0 to 45465
Data columns (total 26 columns):
 #   Column                 Non-Null Count  Dtype  
---  ------                 --------------  -----  
 0   adult                  45346 non-null  object 
 1   belongs_to_collection  4485 non-null   object 
 2   budget                 45346 non-null  int64  
 3   genres                 45346 non-null  object 
 4   homepage               7761 non-null   object 
 5   id                     45346 non-null  object 
 6   imdb_id                45332 non-null  object 
 7   original_language      45335 non-null  object 
 8   original_title         45346 non-null  object 
 9   overview               44405 non-null  object 
 10  popularity             45346 non-null  object 
 11  poster_path            45007 non-null  object 
 12  production_companies   45346 non-null  object 
 13  production_countries   45346 non-null  object 
 14  release_date           45346 non-null  object 
 15  revenue                45346 non-null  float64
 16  runtime                45100 non-null  float64
 17  spoken_languages       45346 non-null  object 
 18  status                 45266 non-null  object 
 19  tagline                20387 non-null  object 
 20  title                  45346 non-null  object 
 21  video                  45346 non-null  object 
 22  vote_average           45346 non-null  float64
 23  vote_count             45346 non-null  float64
 24  year                   45346 non-null  float64
 25  decade                 45346 non-null  float64
dtypes: float64(6), int64(1), object(19)
memory usage: 9.3+ MB
None

Estrutura de Links Small após a limpeza:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 9125 entries, 0 to 9124
Data columns (total 3 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   movieId  9125 non-null   int64  
 1   imdbId   9125 non-null   int64  
 2   tmdbId   9112 non-null   float64
dtypes: float64(1), int64(2)
memory usage: 214.0 KB
None
In [7]:
movies_metadata.columns
Out[7]:
Index(['adult', 'belongs_to_collection', 'budget', 'genres', 'homepage', 'id',
       'imdb_id', 'original_language', 'original_title', 'overview',
       'popularity', 'poster_path', 'production_companies',
       'production_countries', 'release_date', 'revenue', 'runtime',
       'spoken_languages', 'status', 'tagline', 'title', 'video',
       'vote_average', 'vote_count', 'year', 'decade'],
      dtype='object')
In [8]:
movies_metadata.shape
Out[8]:
(45346, 26)
In [9]:
#Removendo colunas desnecessárias para análise
movies_metadata = movies_metadata.drop(['imdb_id','original_title', 'adult', 'poster_path'], axis=1)
In [10]:
#Removendo linhas com valores nulos e criando uma nova coluna 'ROI' que representa a relação entre a receita e o orçamento
movies_metadata['revenue'] = movies_metadata['revenue'].replace(0, np.nan)
movies_metadata['budget'] = movies_metadata['budget'].replace(0, np.nan)
movies_metadata['ROI'] = movies_metadata['revenue'] / movies_metadata['budget'] - 1
movies_metadata['profit'] = movies_metadata['revenue'] - movies_metadata['budget']
In [11]:
# Estatísticas descritivas para o conjunto de dados de metadados de filmes
movies_metadata.describe()
Out[11]:
budget revenue runtime vote_average vote_count year decade ROI profit
count 8.876000e+03 7.397000e+03 45100.000000 45346.000000 45346.000000 45346.000000 45346.000000 5.375000e+03 5.375000e+03
mean 2.162354e+07 6.886594e+07 94.177805 5.624196 110.135293 1991.882834 1987.431747 5.571109e+03 5.928381e+07
std 3.433044e+07 1.465125e+08 38.348775 1.915339 491.899276 24.053040 24.239088 2.169776e+05 1.396003e+08
min 1.000000e+00 1.000000e+00 0.000000 0.000000 0.000000 1874.000000 1870.000000 -9.999995e-01 -1.657101e+08
25% 2.000000e+06 2.401510e+06 85.000000 5.000000 3.000000 1978.000000 1970.000000 -2.128177e-01 -1.456000e+06
50% 8.000000e+06 1.683891e+07 95.000000 6.000000 10.000000 2001.000000 2000.000000 1.057193e+00 1.110167e+07
75% 2.500000e+07 6.731283e+07 107.000000 6.800000 34.000000 2010.000000 2010.000000 3.242222e+00 6.223188e+07
max 3.800000e+08 2.787965e+09 1256.000000 10.000000 14075.000000 2020.000000 2020.000000 1.239638e+07 2.550965e+09
In [12]:
#Filtrando os filmes que tem relação com link_small
movies_metadata['id'] = movies_metadata['id'].astype('int')
movies_metadata = movies_metadata[movies_metadata['id'].isin(links_small_id)]
In [13]:
movies_metadata = pd.merge(movies_metadata, links_small, left_on='id', right_on='tmdbId', how='inner')
In [14]:
movies_metadata
Out[14]:
belongs_to_collection budget genres homepage id original_language overview popularity production_companies production_countries ... video vote_average vote_count year decade ROI profit movieId imdbId tmdbId
0 {'id': 10194, 'name': 'Toy Story Collection', ... 30000000.0 [{'id': 16, 'name': 'Animation'}, {'id': 35, '... http://toystory.disney.com/toy-story 862 en Led by Woody, Andy's toys live happily in his ... 21.946943 [{'name': 'Pixar Animation Studios', 'id': 3}] [{'iso_3166_1': 'US', 'name': 'United States o... ... False 7.7 5415.0 1995.0 1990.0 11.451801 343554033.0 1 114709 862.0
1 NaN 65000000.0 [{'id': 12, 'name': 'Adventure'}, {'id': 14, '... NaN 8844 en When siblings Judy and Peter discover an encha... 17.015539 [{'name': 'TriStar Pictures', 'id': 559}, {'na... [{'iso_3166_1': 'US', 'name': 'United States o... ... False 6.9 2413.0 1995.0 1990.0 3.043035 197797249.0 2 113497 8844.0
2 {'id': 119050, 'name': 'Grumpy Old Men Collect... NaN [{'id': 10749, 'name': 'Romance'}, {'id': 35, ... NaN 15602 en A family wedding reignites the ancient feud be... 11.7129 [{'name': 'Warner Bros.', 'id': 6194}, {'name'... [{'iso_3166_1': 'US', 'name': 'United States o... ... False 6.5 92.0 1995.0 1990.0 NaN NaN 3 113228 15602.0
3 NaN 16000000.0 [{'id': 35, 'name': 'Comedy'}, {'id': 18, 'nam... NaN 31357 en Cheated on, mistreated and stepped on, the wom... 3.859495 [{'name': 'Twentieth Century Fox Film Corporat... [{'iso_3166_1': 'US', 'name': 'United States o... ... False 6.1 34.0 1995.0 1990.0 4.090760 65452156.0 4 114885 31357.0
4 {'id': 96871, 'name': 'Father of the Bride Col... NaN [{'id': 35, 'name': 'Comedy'}] NaN 11862 en Just when George Banks has recovered from his ... 8.387519 [{'name': 'Sandollar Productions', 'id': 5842}... [{'iso_3166_1': 'US', 'name': 'United States o... ... False 5.7 173.0 1995.0 1990.0 NaN NaN 5 113041 11862.0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
9077 NaN 8000000.0 [{'id': 18, 'name': 'Drama'}] NaN 159550 en A man must cope with the loss of his wife and ... 0.038998 [{'name': 'Nasser Entertainment', 'id': 35802}] [{'iso_3166_1': 'US', 'name': 'United States o... ... False 7.0 1.0 2001.0 2000.0 NaN NaN 161944 255313 159550.0
9078 NaN 1000000.0 [{'id': 53, 'name': 'Thriller'}, {'id': 10749,... NaN 392572 hi Rustom Pavri, an honourable officer of the Ind... 7.333139 [{'name': 'KriArj Entertainment', 'id': 91689}] [{'iso_3166_1': 'IN', 'name': 'India'}] ... False 7.3 25.0 2016.0 2010.0 NaN NaN 162542 5165344 392572.0
9079 NaN 15050000.0 [{'id': 12, 'name': 'Adventure'}, {'id': 18, '... NaN 402672 hi Village lad Sarman is drawn to big, bad Mohenj... 1.423358 [{'name': 'UTV Motion Pictures', 'id': 2320}, ... [{'iso_3166_1': 'IN', 'name': 'India'}] ... False 6.7 26.0 2016.0 2010.0 0.075083 1130000.0 162672 3859980 402672.0
9080 NaN 15000000.0 [{'id': 28, 'name': 'Action'}, {'id': 12, 'nam... NaN 315011 ja From the mind behind Evangelion comes a hit la... 9.285519 [{'name': 'Cine Bazar', 'id': 5896}, {'name': ... [{'iso_3166_1': 'JP', 'name': 'Japan'}] ... False 6.6 152.0 2016.0 2010.0 4.133333 62000000.0 163056 4262980 315011.0
9081 NaN NaN [{'id': 99, 'name': 'Documentary'}, {'id': 104... http://www.thebeatlesliveproject.com/ 391698 en The band stormed Europe in 1963, and, in 1964,... 7.078301 [{'name': 'Imagine Entertainment', 'id': 23}, ... [{'iso_3166_1': 'GB', 'name': 'United Kingdom'... ... False 7.6 92.0 2016.0 2010.0 NaN NaN 163949 2531318 391698.0

9082 rows × 27 columns

Ao final da limpeza, temos um dataset movies_metadata contendo as informações sobre os filmes e que já está:¶

  1. Limpo: O dataset não possui linhas faltantes, e os tipos de dados em cada coluna estão corretamente formatados.
  2. Apenas com as colunas principais: O dataset contém as principais colunas que vamos utilizar, incluindo tanto as colunas originais quanto as novas relações criadas durante o processo de limpeza.
  3. Filtrado: O dataset foi filtrado para incluir apenas os principais filmes, conforme a definição do criador do dataset.
In [15]:
movies_metadata.head()
Out[15]:
belongs_to_collection budget genres homepage id original_language overview popularity production_companies production_countries ... video vote_average vote_count year decade ROI profit movieId imdbId tmdbId
0 {'id': 10194, 'name': 'Toy Story Collection', ... 30000000.0 [{'id': 16, 'name': 'Animation'}, {'id': 35, '... http://toystory.disney.com/toy-story 862 en Led by Woody, Andy's toys live happily in his ... 21.946943 [{'name': 'Pixar Animation Studios', 'id': 3}] [{'iso_3166_1': 'US', 'name': 'United States o... ... False 7.7 5415.0 1995.0 1990.0 11.451801 343554033.0 1 114709 862.0
1 NaN 65000000.0 [{'id': 12, 'name': 'Adventure'}, {'id': 14, '... NaN 8844 en When siblings Judy and Peter discover an encha... 17.015539 [{'name': 'TriStar Pictures', 'id': 559}, {'na... [{'iso_3166_1': 'US', 'name': 'United States o... ... False 6.9 2413.0 1995.0 1990.0 3.043035 197797249.0 2 113497 8844.0
2 {'id': 119050, 'name': 'Grumpy Old Men Collect... NaN [{'id': 10749, 'name': 'Romance'}, {'id': 35, ... NaN 15602 en A family wedding reignites the ancient feud be... 11.7129 [{'name': 'Warner Bros.', 'id': 6194}, {'name'... [{'iso_3166_1': 'US', 'name': 'United States o... ... False 6.5 92.0 1995.0 1990.0 NaN NaN 3 113228 15602.0
3 NaN 16000000.0 [{'id': 35, 'name': 'Comedy'}, {'id': 18, 'nam... NaN 31357 en Cheated on, mistreated and stepped on, the wom... 3.859495 [{'name': 'Twentieth Century Fox Film Corporat... [{'iso_3166_1': 'US', 'name': 'United States o... ... False 6.1 34.0 1995.0 1990.0 4.090760 65452156.0 4 114885 31357.0
4 {'id': 96871, 'name': 'Father of the Bride Col... NaN [{'id': 35, 'name': 'Comedy'}] NaN 11862 en Just when George Banks has recovered from his ... 8.387519 [{'name': 'Sandollar Productions', 'id': 5842}... [{'iso_3166_1': 'US', 'name': 'United States o... ... False 5.7 173.0 1995.0 1990.0 NaN NaN 5 113041 11862.0

5 rows × 27 columns

Análise Exploratória¶

Agora que os dados estão preparados, vamos explorar as informações presentes no conjunto de dados. Nesta seção, faremos análises estatísticas descritivas, visualizações gráficas e identificaremos tendências e padrões interessantes.

In [16]:
# Calculando a contagem de filmes por década
decade_count = movies_metadata['decade'].value_counts().sort_index()

# Criando o gráfico de barras
fig = px.bar(decade_count, x=decade_count.index, y=decade_count.values,
             labels={'x': 'Década', 'y': 'Número de Filmes'},
             title='Distribuição de Filmes por Década')

# Ajustando uma linha de tendência exponencial
x_values = decade_count.index
y_values = decade_count.values

# Ajuste de uma curva exponencial aos dados
log_y_values = np.log(y_values + 1)  # Evitando log de zero
z = np.polyfit(x_values, log_y_values, 1)  # Regressão linear sobre o log dos valores
p = np.poly1d(z)

# Função exponencial para a linha de tendência
exp_fit = np.exp(p(x_values))

# Adicionando a linha de tendência ao gráfico
fig.add_traces(go.Scatter(x=x_values, y=exp_fit, mode='lines', name='Tendência Exponencial', line=dict(color='red')))

# Ajustando o layout do gráfico
fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)

# Exibindo o gráfico
fig.show()

Análise da Distribuição de Filmes por Década¶

  1. Tendência de crescimento até 2000: Observamos um crescimento constante na produção de filmes desde as décadas de 1940 até o ano 2000. A linha de tendência exponencial mostra claramente que, até 2000, a produção cinematográfica aumentou consideravelmente, com um salto significativo nas décadas de 1980 e 1990. Esse aumento pode ser associado à evolução da tecnologia, expansão dos mercados de cinema, e maior acesso a recursos de produção e distribuição global.

  2. Crescimento desacelerado de 1990 para 2000: Embora o número de filmes produzidos em 2000 seja maior do que na década de 1990, o crescimento de 1990 para 2000 não foi tão acentuado quanto nos períodos anteriores. Esse comportamento pode indicar que a produção cinematográfica global atingiu um certo patamar de saturação, onde a infraestrutura, custos de produção ou o mercado em si chegaram a um ponto de estabilidade. Outra possibilidade é o início da transição para outras formas de entretenimento, como a popularização de séries de TV e novos formatos digitais.

  3. Produção parcial em 2010 (até 2016): Em 2000, foram produzidos 2533 filmes, enquanto até 2016, foram produzidos 1369 filmes. Como estamos analisando apenas parte da década de 2010, essa queda pode ser explicada pelo fato de que o período analisado ainda não estava completo. Entretanto, essa redução pode levantar questões sobre possíveis mudanças no comportamento de consumo, como o aumento de plataformas de streaming, que podem ter deslocado parte da produção cinematográfica tradicional para novos modelos de mídia. Outro ponto relevante é o avanço de novas tecnologias e o comportamento do mercado após crises econômicas globais, o que pode ter impactado a quantidade de produções.

Em resumo, o gráfico nos dá uma visão clara do crescimento acelerado na produção de filmes até 2000, seguido por uma desaceleração no crescimento e, por fim, uma queda parcial de produções na década de 2010, o que pode estar relacionado a fatores de transição tecnológica e novas demandas de mercado.

In [17]:
fig = px.box(movies_metadata, x='decade', y='vote_average',
             labels={'year': 'Ano', 'vote_average': 'Nota Média'},
             title='Distribuição das Notas dos Filmes por Ano')

fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)

fig.show()

Insight sobre a Distribuição das Notas dos Filmes por Década¶

O gráfico revela que as notas dos filmes permaneceram relativamente estáveis ao longo das décadas e parecem tender a se estabilizar, com a mediana se mantendo entre 6 e 7. No entanto, a partir de 1980, há um aumento na dispersão das notas, com mais filmes recebendo tanto notas muito altas quanto muito baixas, refletindo a maior produção cinematográfica. As décadas anteriores a 1940 mostram uma distribuição mais concentrada, com menos outliers, sugerindo uma produção mais controlada e uniforme em termos de qualidade percebida. Essa análise mostra que, apesar da expansão do mercado, a percepção geral da qualidade dos filmes tem se mantido constante.

In [18]:
movies_metadata['genres'] = movies_metadata['genres'].apply(
    lambda x: [d['name'] for d in eval(x)] if pd.notnull(x) else []
)
genres_df = movies_metadata.explode('genres')

genre_decade_count = genres_df.groupby(['decade', 'genres']).size().reset_index(name='film_count')

fig = px.bar(genre_decade_count, x='decade', y='film_count', color='genres',
             labels={'decade': 'Década', 'film_count': 'Número de Filmes', 'genres': 'Gênero'},
             title='Popularidade dos Gêneros de Filmes por Década')

fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)

fig.show()
In [19]:
def analyze_genre_marketshare_trends(genre_decade_count):
    # Primeiro, criamos um dicionário para armazenar as informações sobre cada gênero
    genre_analysis = {}

    # Calculando o total de filmes por década
    total_per_decade = genre_decade_count.groupby('decade')['film_count'].sum()

    # Para cada gênero, calculamos a participação de mercado ao longo do tempo
    for genre in genre_decade_count['genres'].unique():
        # Filtrar os dados por gênero
        genre_data = genre_decade_count[genre_decade_count['genres'] == genre].sort_values(by='decade')

        # Se tivermos mais de uma década para esse gênero, calculamos a tendência de market share
        if len(genre_data) > 1:
            # Calculando o market share para cada década (filmes do gênero / total de filmes na década)
            genre_data = genre_data.merge(total_per_decade, on='decade', suffixes=('', '_total'))
            genre_data['market_share'] = genre_data['film_count'] / genre_data['film_count_total']

            # Calcula a diferença do market share entre as décadas
            genre_data['market_share_diff'] = genre_data['market_share'].diff().fillna(0)
            total_market_share_growth = genre_data['market_share_diff'].sum()

            # Analisando a variação percentual do market share entre o começo e o fim
            initial_market_share = genre_data['market_share'].iloc[0]
            final_market_share = genre_data['market_share'].iloc[-1]
            initial_market_share_decade = genre_data['decade'].iloc[0]
            final_market_share_decade = genre_data['decade'].iloc[-1]
            growth_rate_market_share = (final_market_share - initial_market_share) / initial_market_share if initial_market_share != 0 else np.nan

            # Calculando o market share máximo, mínimo e as respectivas décadas
            max_market_share = genre_data['market_share'].max()
            max_market_share_decade = genre_data.loc[genre_data['market_share'].idxmax(), 'decade']
            min_market_share = genre_data['market_share'].min()
            min_market_share_decade = genre_data.loc[genre_data['market_share'].idxmin(), 'decade']

            # Adicionando a análise ao dicionário
            genre_analysis[genre] = {
                'total_market_share_growth': total_market_share_growth,
                'growth_rate_market_share': growth_rate_market_share,
                'initial_market_share': initial_market_share,
                'final_market_share': final_market_share,
                'initial_market_share_decade': initial_market_share_decade,
                'final_market_share_decade': final_market_share_decade,
                'max_market_share': max_market_share,
                'max_market_share_decade': max_market_share_decade,
                'min_market_share': min_market_share,
                'min_market_share_decade': min_market_share_decade,
                'trend': 'stable' if abs(growth_rate_market_share) < 0.2 else ('growing' if growth_rate_market_share > 0 else 'declining')
            }

    # Ordenar os gêneros pelo 'growth_rate_market_share' em ordem decrescente
    sorted_genre_analysis = dict(sorted(genre_analysis.items(), key=lambda x: x[1]['growth_rate_market_share'], reverse=True))
    
    return sorted_genre_analysis

# Exemplo de uso da função
genre_decade_count = genres_df.groupby(['decade', 'genres']).size().reset_index(name='film_count')
genre_trends = analyze_genre_marketshare_trends(genre_decade_count)

# Exibindo as informações de tendência de market share ordenadas pelo crescimento
for genre, info in genre_trends.items():
    print(f"Gênero: {genre}")
    print(f" Tendência: {info['trend']}")
    print(f" Crescimento total de Market Share: {info['total_market_share_growth']:.4f}")

    # Exibir o market share inicial
    print(f" Market Share Inicial: {info['initial_market_share']:.4f} (Década: {info['initial_market_share_decade']})")

    # Exibir o market share mínimo ou máximo dependendo da ordem das décadas
    if info['min_market_share_decade'] > info['initial_market_share_decade']:
        print(f" Market Share Mínimo: {info['min_market_share']:.4f} (Década: {info['min_market_share_decade']})")
    if info['max_market_share_decade'] > info['initial_market_share_decade']:
        print(f" Market Share Máximo: {info['max_market_share']:.4f} (Década: {info['max_market_share_decade']})")

    # Exibir o market share final
    print(f" Market Share Final: {info['final_market_share']:.4f} (Década: {info['final_market_share_decade']})\n")
Gênero: Animation
 Tendência: growing
 Crescimento total de Market Share: 0.0207
 Market Share Inicial: 0.0077 (Década: 1920.0)
 Market Share Mínimo: 0.0056 (Década: 1930.0)
 Market Share Máximo: 0.0284 (Década: 2010.0)
 Market Share Final: 0.0284 (Década: 2010.0)

Gênero: Thriller
 Tendência: growing
 Crescimento total de Market Share: 0.0572
 Market Share Inicial: 0.0462 (Década: 1920.0)
 Market Share Mínimo: 0.0362 (Década: 1930.0)
 Market Share Máximo: 0.1034 (Década: 2010.0)
 Market Share Final: 0.1034 (Década: 2010.0)

Gênero: Family
 Tendência: growing
 Crescimento total de Market Share: 0.0162
 Market Share Inicial: 0.0154 (Década: 1920.0)
 Market Share Máximo: 0.0669 (Década: 1930.0)
 Market Share Final: 0.0315 (Década: 2010.0)

Gênero: TV Movie
 Tendência: growing
 Crescimento total de Market Share: 0.0009
 Market Share Inicial: 0.0009 (Década: 1960.0)
 Market Share Mínimo: 0.0003 (Década: 1980.0)
 Market Share Máximo: 0.0023 (Década: 2000.0)
 Market Share Final: 0.0019 (Década: 2010.0)

Gênero: Mystery
 Tendência: growing
 Crescimento total de Market Share: 0.0108
 Market Share Inicial: 0.0154 (Década: 1920.0)
 Market Share Máximo: 0.0605 (Década: 1940.0)
 Market Share Final: 0.0262 (Década: 2010.0)

Gênero: Crime
 Tendência: growing
 Crescimento total de Market Share: 0.0208
 Market Share Inicial: 0.0308 (Década: 1920.0)
 Market Share Máximo: 0.0701 (Década: 1940.0)
 Market Share Final: 0.0515 (Década: 2010.0)

Gênero: Documentary
 Tendência: growing
 Crescimento total de Market Share: 0.0153
 Market Share Inicial: 0.0231 (Década: 1920.0)
 Market Share Mínimo: 0.0024 (Década: 1950.0)
 Market Share Máximo: 0.0384 (Década: 2010.0)
 Market Share Final: 0.0384 (Década: 2010.0)

Gênero: Music
 Tendência: growing
 Crescimento total de Market Share: 0.0042
 Market Share Inicial: 0.0077 (Década: 1920.0)
 Market Share Máximo: 0.0557 (Década: 1930.0)
 Market Share Final: 0.0119 (Década: 2010.0)

Gênero: Action
 Tendência: growing
 Crescimento total de Market Share: 0.0202
 Market Share Inicial: 0.0769 (Década: 1910.0)
 Market Share Mínimo: 0.0318 (Década: 1940.0)
 Market Share Máximo: 0.0971 (Década: 2010.0)
 Market Share Final: 0.0971 (Década: 2010.0)

Gênero: Romance
 Tendência: stable
 Crescimento total de Market Share: -0.0080
 Market Share Inicial: 0.0692 (Década: 1920.0)
 Market Share Mínimo: 0.0573 (Década: 1970.0)
 Market Share Máximo: 0.1393 (Década: 1930.0)
 Market Share Final: 0.0612 (Década: 2010.0)

Gênero: Horror
 Tendência: declining
 Crescimento total de Market Share: -0.0136
 Market Share Inicial: 0.0538 (Década: 1920.0)
 Market Share Mínimo: 0.0304 (Década: 1990.0)
 Market Share Máximo: 0.0683 (Década: 1980.0)
 Market Share Final: 0.0403 (Década: 2010.0)

Gênero: Comedy
 Tendência: declining
 Crescimento total de Market Share: -0.0812
 Market Share Inicial: 0.2308 (Década: 1910.0)
 Market Share Mínimo: 0.0875 (Década: 1950.0)
 Market Share Final: 0.1496 (Década: 2010.0)

Gênero: Drama
 Tendência: declining
 Crescimento total de Market Share: -0.1166
 Market Share Inicial: 0.3077 (Década: 1910.0)
 Market Share Mínimo: 0.1825 (Década: 1980.0)
 Market Share Final: 0.1911 (Década: 2010.0)

Gênero: Foreign
 Tendência: declining
 Crescimento total de Market Share: -0.0025
 Market Share Inicial: 0.0037 (Década: 1960.0)
 Market Share Mínimo: 0.0012 (Década: 2010.0)
 Market Share Máximo: 0.0070 (Década: 1990.0)
 Market Share Final: 0.0012 (Década: 2010.0)

Gênero: Adventure
 Tendência: declining
 Crescimento total de Market Share: -0.2718
 Market Share Inicial: 0.3333 (Década: 1900.0)
 Market Share Mínimo: 0.0239 (Década: 1940.0)
 Market Share Final: 0.0615 (Década: 2010.0)

Gênero: History
 Tendência: declining
 Crescimento total de Market Share: -0.0657
 Market Share Inicial: 0.0769 (Década: 1910.0)
 Market Share Mínimo: 0.0112 (Década: 2010.0)
 Market Share Final: 0.0112 (Década: 2010.0)

Gênero: Science Fiction
 Tendência: declining
 Crescimento total de Market Share: -0.2859
 Market Share Inicial: 0.3333 (Década: 1900.0)
 Market Share Mínimo: 0.0096 (Década: 1940.0)
 Market Share Final: 0.0475 (Década: 2010.0)

Gênero: Fantasy
 Tendência: declining
 Crescimento total de Market Share: -0.2990
 Market Share Inicial: 0.3333 (Década: 1900.0)
 Market Share Mínimo: 0.0195 (Década: 1930.0)
 Market Share Final: 0.0344 (Década: 2010.0)

Gênero: War
 Tendência: declining
 Crescimento total de Market Share: -0.0704
 Market Share Inicial: 0.0769 (Década: 1910.0)
 Market Share Mínimo: 0.0066 (Década: 2010.0)
 Market Share Final: 0.0066 (Década: 2010.0)

Gênero: Western
 Tendência: declining
 Crescimento total de Market Share: -0.0719
 Market Share Inicial: 0.0769 (Década: 1910.0)
 Market Share Mínimo: 0.0026 (Década: 2000.0)
 Market Share Final: 0.0050 (Década: 2010.0)

In [20]:
genre_count = genres_df['genres'].value_counts().reset_index()
genre_count.columns = ['genre', 'film_count']

fig = px.bar(genre_count, x='genre', y='film_count',
             labels={'genre': 'Gênero', 'film_count': 'Número de Filmes'},
             title='Número de Filmes por Gênero')

fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)

fig.show()
In [21]:
movies_metadata['budget'] = pd.to_numeric(movies_metadata['budget'], errors='coerce')

average_budget_per_year = movies_metadata.groupby('year')['budget'].mean().reset_index()

fig = px.line(average_budget_per_year, x='year', y='budget',
              labels={'year': 'Ano', 'budget': 'Orçamento Médio (USD)'},
              title='Evolução dos Orçamentos Médios dos Filmes por Ano')

fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)

fig.show()
In [22]:
# Garantindo que a coluna 'budget' é numérica
movies_metadata['budget'] = pd.to_numeric(movies_metadata['budget'], errors='coerce')

# Filtrando os filmes de 1927
movies_1927 = movies_metadata[movies_metadata['year'] == 1927]

# Verificando o orçamento médio de 1927
budget_1927_avg = movies_1927['budget'].mean()
print(f"Orçamento médio dos filmes em 1927: ${budget_1927_avg:.2f}")

# Exibindo os 5 filmes com os maiores orçamentos de 1927
top_budget_1927 = movies_1927[['title', 'budget', 'genres', 'production_countries', 'production_companies']].sort_values(by='budget', ascending=False).head()
print("Top 5 filmes com maiores orçamentos em 1927:")
print(top_budget_1927)

# Analisando a receita (caso disponível)
if 'revenue' in movies_metadata.columns:
    revenue_1927 = movies_1927['revenue'].mean()
    print(f"Receita média dos filmes de 1927: ${revenue_1927:.2f}")

# Identificando filmes específicos que possam ter influenciado esse aumento
# Investigando outliers de orçamento
outliers_1927 = movies_1927[movies_1927['budget'] > 1.5 * budget_1927_avg]
print("Filmes com orçamentos excepcionalmente altos em 1927:")
print(outliers_1927[['title', 'budget', 'genres', 'production_companies']])

# Identificando o país e estúdios dos filmes
country_count_1927 = movies_1927['production_countries'].value_counts()
print("\nPaíses de produção mais comuns em 1927:")
print(country_count_1927)

studio_count_1927 = movies_1927['production_companies'].value_counts()
print("\nEstúdios de produção mais comuns em 1927:")
print(studio_count_1927)
Orçamento médio dos filmes em 1927: $31544000.00
Top 5 filmes com maiores orçamentos em 1927:
                                      title      budget  \
1568                             Metropolis  92620000.0   
1484                                  Wings   2000000.0   
1758  The Lodger: A Story of the London Fog     12000.0   
5264       Berlin: Symphony of a Great City         NaN   
5323          Sunrise: A Song of Two Humans         NaN   

                             genres  \
1568       [Drama, Science Fiction]   
1484  [Action, Drama, Romance, War]   
1758       [Crime, Drama, Thriller]   
5264         [Documentary, History]   
5323               [Drama, Romance]   

                                   production_countries  \
1568          [{'iso_3166_1': 'DE', 'name': 'Germany'}]   
1484  [{'iso_3166_1': 'US', 'name': 'United States o...   
1758   [{'iso_3166_1': 'GB', 'name': 'United Kingdom'}]   
5264          [{'iso_3166_1': 'DE', 'name': 'Germany'}]   
5323  [{'iso_3166_1': 'US', 'name': 'United States o...   

                                   production_companies  
1568  [{'name': 'Paramount Pictures', 'id': 4}, {'na...  
1484  [{'name': 'Paramount Famous Lasky Corporation'...  
1758  [{'name': 'Carlyle Blackwell Productions', 'id...  
5264  [{'name': 'Deutsche Vereinsfilm AG', 'id': 50}...  
5323  [{'name': 'BIM Distribuzione', 'id': 225}, {'n...  
Receita média dos filmes de 1927: $325271.50
Filmes com orçamentos excepcionalmente altos em 1927:
           title      budget                    genres  \
1568  Metropolis  92620000.0  [Drama, Science Fiction]   

                                   production_companies  
1568  [{'name': 'Paramount Pictures', 'id': 4}, {'na...  

Países de produção mais comuns em 1927:
production_countries
[{'iso_3166_1': 'US', 'name': 'United States of America'}]    4
[{'iso_3166_1': 'DE', 'name': 'Germany'}]                     2
[{'iso_3166_1': 'GB', 'name': 'United Kingdom'}]              1
[{'iso_3166_1': 'FR', 'name': 'France'}]                      1
Name: count, dtype: int64

Estúdios de produção mais comuns em 1927:
production_companies
[]                                                                                                                                                                                                                                                                                 2
[{'name': 'Paramount Famous Lasky Corporation', 'id': 33333}]                                                                                                                                                                                                                      1
[{'name': 'Paramount Pictures', 'id': 4}, {'name': 'Universum Film (UFA)', 'id': 12372}]                                                                                                                                                                                           1
[{'name': 'Carlyle Blackwell Productions', 'id': 1222}]                                                                                                                                                                                                                            1
[{'name': 'Deutsche Vereinsfilm AG', 'id': 50}, {'name': 'Fox Europa Produktion', 'id': 51}]                                                                                                                                                                                       1
[{'name': 'BIM Distribuzione', 'id': 225}, {'name': 'Fox Film Corporation', 'id': 5488}]                                                                                                                                                                                           1
[{'name': 'Pathé Consortium Cinéma', 'id': 220}, {'name': 'Société générale des films', 'id': 1237}, {'name': 'Films Abel Gance', 'id': 9005}, {'name': 'Isepa-Wengeroff Film GmbH', 'id': 9006}, {'name': 'Ciné France', 'id': 27693}, {'name': 'Société Westi', 'id': 82280}]    1
Name: count, dtype: int64
In [23]:
movies_1927 = movies_metadata[movies_metadata['year'] == 1927]
movies_1927_sorted = movies_1927[['title', 'budget']].sort_values(by='budget', ascending=False)

# Plotando o gráfico de colunas verticais
plt.figure(figsize=(10, 6))
plt.bar(movies_1927_sorted['title'], movies_1927_sorted['budget'], color='skyblue')
plt.xlabel('Título', fontsize=12)
plt.ylabel('Orçamento (USD)', fontsize=12)
plt.title('Orçamento dos Filmes de 1927', fontsize=14)
plt.xticks(rotation=45, ha='right', fontsize=10)  # Girar os títulos para melhor visualização
plt.tight_layout()

# Mostrar o gráfico
plt.show()
c:\Users\Eric\anaconda3\Lib\site-packages\IPython\core\pylabtools.py:77: DeprecationWarning:

backend2gui is deprecated since IPython 8.24, backends are managed in matplotlib and can be externally registered.

c:\Users\Eric\anaconda3\Lib\site-packages\IPython\core\pylabtools.py:77: DeprecationWarning:

backend2gui is deprecated since IPython 8.24, backends are managed in matplotlib and can be externally registered.

c:\Users\Eric\anaconda3\Lib\site-packages\IPython\core\pylabtools.py:77: DeprecationWarning:

backend2gui is deprecated since IPython 8.24, backends are managed in matplotlib and can be externally registered.

No description has been provided for this image

Análise da Evolução dos Orçamentos Médios dos Filmes por Ano¶

O gráfico acima mostra a evolução dos orçamentos médios dos filmes ao longo dos anos, em dólares americanos. Podemos observar que, até a década de 1960, os orçamentos dos filmes mantiveram-se relativamente estáveis, com uma tendência de crescimento mais acentuada a partir da década de 1980.

Destaque: 1927 como um Outlier¶

Um ponto notável é o pico em 1927, que se destaca como um outlier significativo. Esse aumento drástico no orçamento médio é explicado pelo fato de que, nesse ano, apenas três filmes tiveram seus orçamentos registrados. Um dos filmes mais importantes desse período foi Metropolis, uma das maiores produções cinematográficas da época, conhecida por seu orçamento extraordinariamente alto para os padrões da época.

In [24]:
fig = px.scatter(movies_metadata, x='budget', y='vote_average',
                 labels={'budget': 'Orçamento (USD)', 'vote_average': 'Nota Média'},
                 title='Relação entre Orçamento e Nota Média dos Filmes')

fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)

fig.show()

Análise da Relação entre Orçamento e Nota Média dos Filmes¶

O gráfico acima ilustra a relação entre o orçamento (em dólares) e a nota média dos filmes. Podemos observar algumas tendências interessantes ao analisar esses dados:

Observações Principais:¶

  1. Forma de Funil:

    • À medida que o orçamento dos filmes aumenta, a dispersão das notas médias parece se afunilar. Isso significa que, embora filmes de baixo orçamento possam ter notas muito variadas (de notas muito baixas até notas muito altas), filmes de orçamento mais elevado tendem a ter suas notas concentradas em uma faixa mais estreita, geralmente entre 5 e 8.
  2. Maior Estabilidade em Filmes de Alto Orçamento:

    • Filmes com orçamentos mais altos, especialmente aqueles com mais de 200 milhões de dólares, mostram uma menor variabilidade nas notas médias. Esses filmes tendem a se manter na média, com poucas chances de obterem notas muito baixas ou muito altas. Isso indica que, embora o alto investimento não garanta uma nota extremamente alta, ele minimiza o risco de fracasso crítico.
  3. Alta Variabilidade em Filmes de Baixo Orçamento:

    • Por outro lado, filmes de baixo orçamento (menos de 50 milhões de dólares) apresentam uma variabilidade significativa nas notas, com exemplos que vão de grandes sucessos críticos (notas acima de 8) até fracassos (notas abaixo de 3).

Hipótese:¶

Com base na forma de funil observada no gráfico, podemos levantar a seguinte hipótese: filmes de alto orçamento tendem a ser projetos mais "seguros" em termos de qualidade percebida, o que resulta em notas médias mais previsíveis. Isso pode ocorrer porque grandes estúdios com altos orçamentos têm mais recursos para investir em equipes experientes, efeitos especiais de ponta e campanhas de marketing, o que contribui para garantir um padrão de qualidade mais consistente, evitando notas muito baixas. No entanto, para garantir o resultado do filme, o lado criativo dos filmes pode acabar sendo deixado de lado e, ao tomar menos riscos, a possibilidade de notas a cima ou abaixo da média tende a cair.

Por outro lado, filmes de baixo orçamento parecem representar apostas mais arriscadas, onde os resultados podem variar amplamente. Nessa faixa, projetos inovadores ou independentes podem alcançar grande sucesso crítico, enquanto filmes com menos recursos ou apelo comercial podem ser mal avaliados.

In [25]:
movies_metadata['countries'] = movies_metadata['production_countries'].apply(
    lambda x: [d['name'] for d in eval(x)] if pd.notnull(x) else []
)

countries_df = movies_metadata.explode('countries')
country_counts = countries_df['countries'].value_counts().reset_index()
country_counts.columns = ['country', 'film_count']

fig = px.choropleth(country_counts,
                    locations="country",
                    locationmode='country names',
                    color="film_count",
                    hover_name="country",
                    color_continuous_scale=px.colors.sequential.Reds,
                    labels={'film_count': 'Número de Filmes'},
                    title="Número de Filmes por País")

fig.update_layout(
    title_font_size=20,
    geo=dict(
        showframe=False,
        showcoastlines=False,
        projection_type='natural earth', 
        showland=True,
        landcolor='white',
        showlakes=True,
        lakecolor='lightblue',
        subunitwidth=0.5,
        countrywidth=0.5
    ),
    margin={"r":0,"t":50,"l":0,"b":0}, 
)

fig.show()
In [26]:
country_counts
Out[26]:
country film_count
0 United States of America 6847
1 United Kingdom 1205
2 France 698
3 Germany 541
4 Canada 359
... ... ...
90 Liechtenstein 1
91 Czechoslovakia 1
92 Cameroon 1
93 Nepal 1
94 Sri Lanka 1

95 rows × 2 columns

In [27]:
movies_metadata_filtered = movies_metadata[(movies_metadata['budget'] > 0) & (movies_metadata['revenue'] > 0)]

fig = px.scatter(movies_metadata_filtered, x='budget', y='revenue', 
                 labels={'budget': 'Orçamento (USD)', 'revenue': 'Receita (USD)'},
                 title='Orçamento vs Receita')

fig.update_xaxes(type="log", matches='y')
fig.update_yaxes(type="log", matches='x')

fig.add_shape(
    type="line",
    x0=min(movies_metadata_filtered['budget']),
    y0=min(movies_metadata_filtered['budget']),
    x1=max(movies_metadata_filtered['budget']),
    y1=max(movies_metadata_filtered['budget']),
    line=dict(color="red", width=2, dash="dash"),
)

fig.update_layout(
    title_font_size=16,
    xaxis_title_font_size=14,
    yaxis_title_font_size=14,
    width=800,
    height=600,
    showlegend=False
)

fig.show()

Análise do Gráfico de Orçamento vs Receita¶

O gráfico acima mostra a relação entre o orçamento (em USD) e a receita dos filmes. Podemos observar uma correlação positiva clara entre os dois, o que indica que, em geral, filmes com maior orçamento tendem a gerar maior receita. A linha pontilhada em vermelho indica essa tendência um filme que teve a receita igual ao orçamento, e como podemos ver, a maioria dos filmes ficam a cima dessa linha. Além dissom as chances de se estar a cima dessa linha cresciem conforme o seu orçamento é maior.

In [28]:
print(movies_metadata['genres'].head())
0     [Animation, Comedy, Family]
1    [Adventure, Fantasy, Family]
2               [Romance, Comedy]
3        [Comedy, Drama, Romance]
4                        [Comedy]
Name: genres, dtype: object
In [29]:
from itertools import combinations

movies_metadata = movies_metadata[movies_metadata['genres'].apply(len) > 0]

genre_combinations = movies_metadata['genres'].apply(lambda x: list(combinations(x, 2)))

flat_combinations = [item for sublist in genre_combinations for item in sublist]

cooccurrence_matrix = pd.DataFrame(flat_combinations, columns=['Genre1', 'Genre2'])
cooccurrence_matrix['Cooccurrence'] = 1

cooccurrence_pivot = cooccurrence_matrix.pivot_table(index='Genre1', columns='Genre2', values='Cooccurrence', aggfunc='sum', fill_value=0)

genre_counts = movies_metadata['genres'].explode().value_counts()

for genre in genre_counts.index:
    cooccurrence_pivot.loc[genre, genre] = genre_counts[genre]

for genre in cooccurrence_pivot.index:
    cooccurrence_pivot.loc[genre] = cooccurrence_pivot.loc[genre] / genre_counts[genre]

for genre in cooccurrence_pivot.index:
    if genre not in cooccurrence_pivot.columns:
        cooccurrence_pivot[genre] = 0

for genre in cooccurrence_pivot.columns:
    if genre not in cooccurrence_pivot.index:
        cooccurrence_pivot.loc[genre] = 0

cooccurrence_pivot = cooccurrence_pivot.sort_index(axis=0).sort_index(axis=1)

fig = px.imshow(cooccurrence_pivot,
                labels=dict(x="Gênero", y="Gênero", color="Proporção de Coocorrência"),
                x=cooccurrence_pivot.columns,
                y=cooccurrence_pivot.index,
                color_continuous_scale="Blues",
                title="Matriz de Coocorrência Normalizada de Gêneros de Filmes")

fig.update_layout(
    title_font_size=20,
    xaxis_title_font_size=16,
    yaxis_title_font_size=16,
    width=1000,
    height=1000,
    xaxis=dict(tickangle=-45),
)

fig.show()
C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.27837992013690815' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.027381631488876214' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.23445521962350258' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.2316029663434113' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.0017113519680547634' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.24472333143183114' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.05989731888191671' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.05932686822589846' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.005704506560182544' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.03479749001711352' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.037079292641186534' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.008556759840273816' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.04677695379349686' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.05932686822589846' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.1791215059897319' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.41186537364517967' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.03993154592127781' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.034227039361095266' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.17042606516290726' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\212288003.py:20: FutureWarning:

Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '0.002506265664160401' has dtype incompatible with int64, please explicitly cast to a compatible dtype first.

In [30]:
# Extraindo os pares de coocorrência e seus valores resultantes em um DataFrame
cooccurrence_flat = cooccurrence_pivot.stack().reset_index()
cooccurrence_flat.columns = ['Genre1', 'Genre2', 'Cooccurrence Value']

# Filtrando os pares com valor > 0 e onde Genre1 e Genre2 são diferentes, e ordenando pelos valores de coocorrência em ordem decrescente
cooccurrence_flat_sorted = cooccurrence_flat[
    (cooccurrence_flat['Cooccurrence Value'] > 0) & (cooccurrence_flat['Genre1'] != cooccurrence_flat['Genre2'])
].sort_values(by='Cooccurrence Value', ascending=False)

# Exibindo os resultados ordenados
print(cooccurrence_flat_sorted)
        Genre1       Genre2  Cooccurrence Value
47   Animation       Family            0.559091
277    Mystery     Thriller            0.430108
86       Crime        Drama            0.419124
237     Horror     Thriller            0.417666
17      Action     Thriller            0.411865
..         ...          ...                 ...
296    Romance     TV Movie            0.001082
342   Thriller    Animation            0.000992
345   Thriller  Documentary            0.000992
29   Adventure      Foreign            0.000835
356   Thriller     TV Movie            0.000496

[336 rows x 3 columns]

Análise da Matriz de Coocorrência de Gêneros de Filmes¶

A matriz de coocorrência apresentada foi construída para mostrar a proporção de vezes que dois gêneros de filmes aparecem juntos. Para cada par de gêneros, o valor na matriz representa a frequência relativa com que os dois aparecem no mesmo filme, normalizada pela quantidade total de filmes de cada gênero.

Interpretação do Resultado:¶

  • Gêneros com Alta Coocorrência: Gêneros com valores de coocorrência mais altos (próximos de 1) indicam que esses dois gêneros aparecem frequentemente juntos. Isso sugere que há uma correlação forte entre esses gêneros, ou seja, os filmes que pertencem a um desses gêneros tendem a pertencer também ao outro.

  • Gêneros com Baixa Coocorrência: Gêneros com valores mais baixos indicam que esses dois gêneros aparecem juntos com menos frequência. Quando o valor de coocorrência é 0, significa que não há filmes que pertencem a ambos os gêneros ao mesmo tempo.

Exemplos de Análise:¶

  • Se o par de gêneros "Ação" e "Aventura" tiver um valor de coocorrência alto, isso sugere que filmes de Ação frequentemente também são classificados como Aventura, o que faz sentido dado que esses gêneros muitas vezes compartilham temas similares.

  • Por outro lado, se o par "Documentário" e "Comédia" tiver um valor de coocorrência muito baixo ou nulo, isso indica que é raro encontrar filmes que pertençam simultaneamente a esses dois gêneros, pois eles têm temas e características bastante diferentes.

Aplicabilidade:¶

Essa análise pode ser utilizada para entender melhor padrões de combinação de gêneros em filmes e pode ser útil em diversas áreas, como Recomendações de filmes, para identificar gêneros que frequentemente aparecem juntos pode ajudar a sugerir filmes de gêneros correlacionados.

In [31]:
correlation_matrix = movies_metadata[['budget', 'revenue', 'runtime', 'vote_average', 'popularity', 'profit']].corr(method='pearson')

correlation_matrix = correlation_matrix.round(2)

fig = px.imshow(correlation_matrix,
                text_auto=True,
                color_continuous_scale='RdBu_r',
                labels=dict(x="Attributes", y="Attributes", color="Correlation"),
                title="Matriz de Correlação entre Atributos")

fig.update_layout(
    width=800,
    height=600,
    xaxis_title="Atributos",
    yaxis_title="Atributos"
)

fig.show()

Análise da Matriz de Correlação entre Atributos de Filmes¶

A matriz de correlação apresentada mostra a relação entre diferentes atributos de filmes, como orçamento (budget), receita (revenue), tempo de execução (runtime), nota média (vote_average), e popularidade (popularity). A correlação varia de -1 (correlação negativa perfeita) a 1 (correlação positiva perfeita), onde valores próximos de 0 indicam pouca ou nenhuma correlação.

Principais Insights:¶

  1. Forte Correlação entre Orçamento e Receita (0.72):

    • Existe uma correlação forte e positiva entre o orçamento e a receita. Isso indica que, em geral, filmes com orçamentos mais altos tendem a gerar receitas maiores. Esta é uma tendência comum na indústria cinematográfica, onde produções maiores têm maior capacidade de atrair público e gerar retornos significativos.
  2. Correlação entre Popularidade e Receita (0.43):

    • A popularidade também tem uma correlação moderada com a receita (0.43), sugerindo que filmes mais populares conseguem gerar receitas mais altas. No entanto, essa correlação não é tão alta quanto a de orçamento e receita, o que implica que, mesmo com grande popularidade, fatores como orçamento e distribuição podem ter um peso maior no sucesso financeiro.
  3. Correlação entre Tempo de Execução e Orçamento (0.22):

    • Há uma correlação positiva moderada entre o tempo de execução (runtime) e o orçamento. Filmes mais longos tendem a ter mais orçamento, o que pode estar relacionado ao fato de que filmes de maior duração frequentemente são grandes produções.
  4. Baixa Correlação entre Nota Média e Orçamento (-0.08):

    • A correlação entre orçamento e nota média (vote_average) é praticamente nula e até ligeiramente negativa. Isso significa que gastar mais dinheiro em um filme não garante que ele será bem avaliado pela crítica ou pelo público. De fato, muitos filmes de grande orçamento podem ter foco em aspectos comerciais, enquanto filmes de baixo orçamento podem alcançar grande sucesso crítico.
  5. Popularidade e Orçamento (0.27):

    • A popularidade está moderadamente correlacionada com o orçamento. Isso indica que filmes com orçamentos maiores tendem a ser mais populares, mas não é uma correlação muito forte. Isso sugere que, embora o orçamento ajude a aumentar a visibilidade do filme, a popularidade também depende de outros fatores, como que podem estar relacionados possivelmente com atores, marketing e etc.
  6. Correlação Levemente Alta entre Orçamento e Lucro (0.57):

    • Existe uma correlação positiva levemente alta entre orçamento e lucro. Isso sugere que, em geral, filmes com orçamentos mais altos têm uma tendência a gerar lucros maiores, embora não seja uma garantia absoluta.
In [32]:
movies_metadata['profit'] = movies_metadata['revenue'] - movies_metadata['budget']

movies_metadata['release_date'] = pd.to_datetime(movies_metadata['release_date'], errors='coerce')
movies_metadata['release_month'] = movies_metadata['release_date'].dt.month

genres_exploded = movies_metadata.explode('genres')

profit_per_genre_month = genres_exploded.groupby(['genres', 'release_month'])['profit'].mean().reset_index()

fig = px.line(profit_per_genre_month, x='release_month', y='profit', color='genres',
              title='Média de Lucro por Mês para Cada Gênero',
              labels={'release_month': 'Mês de Lançamento', 'profit': 'Média de Lucro'},
              markers=True)

fig.update_layout(
    xaxis_title='Mês de Lançamento',
    yaxis_title='Média de Lucro',
    title_x=0.5,
    width=900,
    height=600
)

fig.show()
C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\2426416435.py:1: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\2426416435.py:3: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\2426416435.py:4: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

In [33]:
movies_metadata['profit'] = movies_metadata['revenue'] - movies_metadata['budget']

movies_metadata['release_date'] = pd.to_datetime(movies_metadata['release_date'], errors='coerce')
movies_metadata['release_month'] = movies_metadata['release_date'].dt.month

profit_per_month = movies_metadata.groupby('release_month')['profit'].mean().reset_index()

fig = px.line(profit_per_month, x='release_month', y='profit',
              title='Média de Lucro por Mês de Lançamento',
              labels={'release_month': 'Mês de Lançamento', 'profit': 'Média de Lucro'},
              markers=True)

fig.update_layout(
    xaxis_title='Mês de Lançamento',
    yaxis_title='Média de Lucro',
    title_x=0.5,
    width=900,
    height=600
)

fig.show()
C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\2854691627.py:1: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\2854691627.py:3: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\2854691627.py:4: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

Análise da Média de Lucro por Mês de Lançamento¶

O gráfico mostra a quantidade de lucro gerada por um filme baseado no mês em que foi lançado. Os números variam entre aproximadamente 29 milhões de dólares (Janeiro) até 132 milhões de dólares (Junho).

Insight:¶

Gêneros de filme que seguem o padrão do gráfico:

  • Reparamos que existem alguns gêneros de filme que seguem o padrão do gráfico, que inclui todos os gêneros de forma bem parecida, com destaque para os gêneros de família e animação. Como hipótese, temos que o período de férias escolares americanas se inicia aproximadamente na metade de maio e se estende até o fim de agosto, sendo uma grande fonte de público, principalmente para filmes de família e animação, que, comumente, atraem um público mais jovem.
  • Já filmes com gêneros como horror e documentário tendem a seguir padrões de lançamento e lucro diferentes.
In [34]:
movies_metadata['budget'] = pd.to_numeric(movies_metadata['budget'], errors='coerce')
movies_metadata['revenue'] = pd.to_numeric(movies_metadata['revenue'], errors='coerce')

movies_metadata = movies_metadata[(movies_metadata['budget'] > 0) & (movies_metadata['revenue'] >= 0)].copy()

movies_metadata['is_flop'] = movies_metadata['revenue'] < movies_metadata['budget']

movies_metadata['genres'] = movies_metadata['genres'].apply(lambda x: x if isinstance(x, list) else [])
genres_exploded = movies_metadata.explode('genres')

# 4. Count Flops per Genre
flop_movies = genres_exploded[genres_exploded['is_flop'] == True]
flop_counts = flop_movies['genres'].value_counts().reset_index()
flop_counts.columns = ['Genre', 'Number of Flops']

fig = px.bar(
    flop_counts,
    x='Genre',
    y='Number of Flops',
    title='Number of Flops per Genre',
    labels={'Genre': 'Gênero', 'Number of Flops': 'Número de Flops'},
)

fig.update_traces(marker_color='blue')  # Definindo a cor azul para todas as barras

fig.update_layout(
    xaxis_title='Gênero',
    yaxis_title='Número de Flops',
    title_x=0.5,
    xaxis_tickangle=-45,
    width=900,
    height=600
)

fig.show()
C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\3357064704.py:1: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\3357064704.py:2: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

In [35]:
fig = px.bar(genre_count, x='genre', y='film_count',
             labels={'genre': 'Gênero', 'film_count': 'Número de Filmes'},
             title='Número de Filmes por Gênero')

fig.update_layout(title_font_size=16, xaxis_title_font_size=14, yaxis_title_font_size=14)

fig.show()

Nenhuma informação pode ser concluida a partir do número de flops, tendo em vista que ele está muito relacionado com o número de filmes feitos.

In [36]:
movies_metadata['budget'] = pd.to_numeric(movies_metadata['budget'], errors='coerce')
movies_metadata['revenue'] = pd.to_numeric(movies_metadata['revenue'], errors='coerce')

movies_metadata = movies_metadata[(movies_metadata['budget'] > 0) & (movies_metadata['revenue'] >= 0)].copy()

movies_metadata['is_flop'] = movies_metadata['revenue'] < movies_metadata['budget']

movies_metadata['genres'] = movies_metadata['genres'].apply(lambda x: x if isinstance(x, list) else [])
genres_exploded = movies_metadata.explode('genres')

total_movies_per_genre = genres_exploded['genres'].value_counts().reset_index()
total_movies_per_genre.columns = ['Genre', 'Total Movies']

flop_movies = genres_exploded[genres_exploded['is_flop'] == True]
flop_counts = flop_movies['genres'].value_counts().reset_index()
flop_counts.columns = ['Genre', 'Number of Flops']

flop_proportion = flop_counts.merge(total_movies_per_genre, on='Genre')
flop_proportion['Flop Proportion'] = flop_proportion['Number of Flops'] / flop_proportion['Total Movies']

flop_proportion = flop_proportion.sort_values(by='Flop Proportion', ascending=False)

fig = px.bar(
    flop_proportion,
    x='Genre',
    y='Flop Proportion',
    title='Proporção de Flops por Gênero (Ordenado do Maior para o Menor)',
    labels={'Genre': 'Gênero', 'Flop Proportion': 'Proporção de Flops'},
)

fig.update_traces(marker_color='blue')

fig.update_layout(
    xaxis_title='Gênero',
    yaxis_title='Proporção de Flops',
    title_x=0.5,
    xaxis_tickangle=-45,
    width=900,
    height=600
)

fig.show()

Proporção de Flops por Gênero¶

Apesar do gráfico apresentar as proporções de filmes que floppam em cada gênero, não é possível tirar conclusões definitivas sobre a relação entre o gênero de um filme e a probabilidade de ele floppar. Há muitos fatores envolvidos no desempenho financeiro de um filme, como o orçamento, marketing, recepção crítica, concorrência no mercado, entre outros. Portanto, não se pode inferir que determinados gêneros estão mais propensos a floppar apenas com base nesses dados.

Seria necessário uma análise mais profunda, considerando outros fatores que influenciam o sucesso ou fracasso de um filme.

In [37]:
movies_metadata['is_flop'] = (movies_metadata['revenue'] < movies_metadata['budget']).astype(int)

movies_metadata['popularity'] = pd.to_numeric(movies_metadata['popularity'], errors='coerce')

# Remover filmes com valores ausentes nas métricas analisadas
movies_metadata_clean = movies_metadata.dropna(subset=['budget', 'revenue', 'vote_average', 'runtime', 'popularity'])

# Criar gráfico de coordenadas paralelas com 'is_flop' como dimensão final
fig_parallel_flop = px.parallel_coordinates(
    movies_metadata_clean,
    dimensions=['budget', 'revenue', 'vote_average', 'runtime', 'popularity', 'is_flop'],
    color='is_flop',
    color_continuous_scale=px.colors.diverging.Tealrose,
    title="Análise de Filmes Flop vs Não Flop em Coordenadas Paralelas (com Flop)",
    labels={'budget': 'Orçamento (em $)', 'revenue': 'Receita (em $)', 'vote_average': 'Média de Votos',
            'runtime': 'Duração (min)', 'popularity': 'Popularidade', 'is_flop': 'É Flop?'}
)

# Mostrar o gráfico de coordenadas paralelas atualizado
fig_parallel_flop.show()
In [38]:
# Definir o número de grupos
num_grupos = 100

# Criar uma função para agrupar os dados por flop e não flop, e calcular médias para cada subgrupo
def gerar_grupos_aleatorios(df, num_grupos):
    # Selecionar apenas as colunas numéricas para evitar erros
    numeric_cols = df.select_dtypes(include=[np.number])
    
    # Separar filmes flops e não flops
    flop = numeric_cols[df['is_flop'] == 1]
    non_flop = numeric_cols[df['is_flop'] == 0]
    
    # Dividir os dados de flop e não flop em partes iguais
    flop_grupos = np.array_split(flop, num_grupos // 2)
    non_flop_grupos = np.array_split(non_flop, num_grupos // 2)
    
    # Lista para armazenar os grupos
    grupos_lista = []
    
    # Calcular a média para cada subgrupo de flop e não flop
    for flop_grupo in flop_grupos:
        grupo_media = flop_grupo.mean().to_frame().T
        grupo_media['is_flop'] = 1  # Marcador de flop
        grupos_lista.append(grupo_media)
    
    for non_flop_grupo in non_flop_grupos:
        grupo_media = non_flop_grupo.mean().to_frame().T
        grupo_media['is_flop'] = 0  # Marcador de não flop
        grupos_lista.append(grupo_media)
    
    # Concatenar todos os grupos
    grupos = pd.concat(grupos_lista, ignore_index=True)
    return grupos

# Gerar os grupos aleatórios com médias
movies_metadata_grupos = gerar_grupos_aleatorios(movies_metadata_clean, num_grupos)

# Criar gráfico de coordenadas paralelas com os grupos gerados
fig_grupos_parallel = px.parallel_coordinates(
    movies_metadata_grupos,
    dimensions=['budget', 'revenue', 'vote_average', 'runtime', 'popularity', 'is_flop'],
    color='is_flop',
    color_continuous_scale=px.colors.diverging.Tealrose,
    title=f"Análise de Filmes Flop vs Não Flop (Médias de {num_grupos} Grupos)",
    labels={'budget': 'Orçamento (em $)', 'revenue': 'Receita (em $)', 'vote_average': 'Média de Votos',
            'runtime': 'Duração (min)', 'popularity': 'Popularidade', 'is_flop': 'É Flop?'}
)

# Mostrar o gráfico com grupos de médias
fig_grupos_parallel.show()
c:\Users\Eric\anaconda3\Lib\site-packages\numpy\core\fromnumeric.py:59: FutureWarning:

'DataFrame.swapaxes' is deprecated and will be removed in a future version. Please use 'DataFrame.transpose' instead.

Insights Principais:¶

  1. Orçamento e Receita:

    • Filmes classificados como "não flop" apresentam receitas significativamente maiores, muitas vezes superando os $150M, enquanto os filmes "flop" têm receitas bem inferiores, frequentemente abaixo de $50M, independentemente do orçamento. Essa relação vem da própria natureza do que é ser flop, visto que flops sõ definidos como receita < orçamento.
  2. Média de Votos:

    • Filmes que não são flop tendem a ter uma média de votos mais alta, geralmente acima de 6.2, enquanto os flops tendem a se concentrar em médias de votos abaixo de 6.0.
  3. Duração:

    • A duração dos filmes não parece ser um fator decisivo na distinção entre flop e não flop, com ambos os grupos apresentando uma distribuição similar entre 100 e 120 minutos. No entando parece que filmes como uma duração muito pequena (menos de 1 hora e meia) tendem a ser flops.
  4. Popularidade:

    • Filmes "não flop" são, em média, mais populares, com valores de popularidade mais elevados, enquanto os flops têm popularidade baixa, abaixo de 10 na maioria dos casos.
  5. Padrões Gerais:

    • As linhas que representam os grupos de filmes flop tendem a se concentrar em valores mais baixos em quase todas as dimensões (receita, orçamento, popularidade), enquanto os filmes de sucesso se destacam nessas variáveis.
In [39]:
# Função para remover os 5% superiores e inferiores de ROI
def remover_extremos(df, column):
    lower_bound = df[column].quantile(0.1)
    upper_bound = df[column].quantile(0.9)
    return df[(df[column] >= lower_bound) & (df[column] <= upper_bound)]

# Remover os 5% superiores e inferiores de ROI
movies_metadata_clean = remover_extremos(movies_metadata_clean, 'ROI')

# Criar gráfico de coordenadas paralelas com 'is_flop' como dimensão final
fig_parallel_flop = px.parallel_coordinates(
    movies_metadata_clean,
    dimensions=['budget', 'revenue', 'vote_average', 'runtime', 'popularity', 'ROI'],
    color='ROI',
    color_continuous_scale=px.colors.diverging.Tealrose,
    title="Análise de ROI em Coordenadas Paralelas (com ROI)",
    labels={'budget': 'Orçamento (em $)', 'revenue': 'Receita (em $)', 'vote_average': 'Média de Votos',
            'runtime': 'Duração (min)', 'popularity': 'Popularidade', 'ROI': 'ROI'}
)

# Mostrar o gráfico de coordenadas paralelas atualizado
fig_parallel_flop.show()
In [40]:
# Definir o número de grupos
num_grupos = 500

# Função para agrupar os dados por flop e não flop, e calcular médias para cada subgrupo
def gerar_grupos_aleatorios(df, num_grupos):
    # Selecionar apenas as colunas numéricas para evitar erros
    numeric_cols = df.select_dtypes(include=[np.number])
    
    # Separar filmes flops (ROI < 0) e não flops (ROI >= 0)
    flop = numeric_cols[df['ROI'] < 0]
    non_flop = numeric_cols[df['ROI'] >= 0]
    
    # Dividir os dados de flop e não flop em partes iguais
    flop_grupos = np.array_split(flop, num_grupos // 2)
    non_flop_grupos = np.array_split(non_flop, num_grupos // 2)
    
    # Lista para armazenar os grupos
    grupos_lista = []
    
    # Calcular a média para cada subgrupo de flop e não flop
    for flop_grupo in flop_grupos:
        grupo_media = flop_grupo.mean().to_frame().T
        grupos_lista.append(grupo_media)
    
    for non_flop_grupo in non_flop_grupos:
        grupo_media = non_flop_grupo.mean().to_frame().T
        grupos_lista.append(grupo_media)
    
    # Concatenar todos os grupos
    grupos = pd.concat(grupos_lista, ignore_index=True)
    return grupos

# Gerar os grupos aleatórios com médias
movies_metadata_grupos = gerar_grupos_aleatorios(movies_metadata_clean, num_grupos)

# Criar gráfico de coordenadas paralelas com os grupos gerados
fig_grupos_parallel = px.parallel_coordinates(
    movies_metadata_grupos,
    dimensions=['budget', 'revenue', 'vote_average', 'runtime', 'popularity', 'ROI'],  # Agora usando o valor real de ROI
    color='ROI',  # ROI como valor contínuo
    color_continuous_scale=px.colors.diverging.Tealrose,
    title=f"Análise de ROI (Médias de {num_grupos} Grupos, sem 5% superiores e inferiores de ROI)",
    labels={'budget': 'Orçamento (em $)', 'revenue': 'Receita (em $)', 'vote_average': 'Média de Votos',
            'runtime': 'Duração (min)', 'popularity': 'Popularidade', 'ROI': 'ROI'}
)

# Mostrar o gráfico com grupos de médias
fig_grupos_parallel.show()
c:\Users\Eric\anaconda3\Lib\site-packages\numpy\core\fromnumeric.py:59: FutureWarning:

'DataFrame.swapaxes' is deprecated and will be removed in a future version. Please use 'DataFrame.transpose' instead.

Insights sobre o ROI:¶

  1. Orçamento e Receita:

    • Grupos com altos retornos sobre investimento (ROI) tendem a ter receitas significativamente maiores, chegando a mais de $600M, mesmo quando o orçamento é consideravelmente menor, indicando uma eficiência no uso do orçamento.
    • Por outro lado, grupos com ROI negativo ou muito baixo geralmente possuem receitas abaixo de $100M, mesmo quando o orçamento foi elevado.
  2. Média de Votos:

    • Filmes com menor ROI também tendem a ter uma média de votos menor que 6.0, enquanto filmes com notas maiores que 6.0 estão bastante distribuidos com relação ao ROI.
  3. Duração:

    • Filmes com durações menos longas (abaixo de 95 minutos) parecem estar associados a ROI negativo, enquanto filmes mais longos apresentam uma maior variabilidade, com muitos grupos de ROI baixo e alto.
  4. Popularidade:

    • Filmes com alta popularidade, especialmente acima de 40, estão mais correlacionados com ROI positivo, enquanto grupos com ROI negativo tendem a ter popularidade abaixo de 10.

Parece que é simples encontras características que identificam que um ROI nãp é alto, no entanto é difícil entender as características que fazem um ROI ser alto.

In [41]:
#Ensure 'budget' is numeric
movies_metadata['budget'] = pd.to_numeric(movies_metadata['budget'], errors='coerce')

# Remove movies with missing or zero budget
movies_metadata = movies_metadata[movies_metadata['budget'] > 0].copy()

#Explode Genres (Each genre gets its own row)
movies_metadata['genres'] = movies_metadata['genres'].apply(lambda x: x if isinstance(x, list) else [])
genres_exploded = movies_metadata.explode('genres')

#Group by genre and calculate the average budget
average_budget_per_genre = genres_exploded.groupby('genres')['budget'].mean().reset_index()

#Sort by average budget in descending order
average_budget_per_genre = average_budget_per_genre.sort_values(by='budget', ascending=False)

#Visualize the average budget per genre
fig = px.bar(
    average_budget_per_genre,
    x='genres',
    y='budget',
    title='Genres with the Highest Average Budget',
    labels={'genres': 'Gênero', 'budget': 'Orçamento Médio (USD)'},
)

# Setting all bars to the same color
fig.update_traces(marker_color='blue')

# Adjusting layout
fig.update_layout(
    xaxis_title='Gênero',
    yaxis_title='Orçamento Médio (USD)',
    title_x=0.5,
    xaxis_tickangle=-45,
    width=900,
    height=600
)

fig.show()

Análise do Gráfico de Orçamento Médio por Gênero¶

O gráfico acima mostra o orçamento médio (em dólares) dos filmes, separados por gênero. Aqui estão algumas conclusões possíveis a partir dos dados apresentados:

Principais Insights:¶

  1. Gêneros com Maior Orçamento Médio:

    • Animação e Aventura lideram como os gêneros com o maior orçamento médio, ambos ultrapassando os 60 milhões de dólares. Isso faz sentido, já que filmes de animação frequentemente envolvem grandes equipes de artistas, longos períodos de produção e tecnologias avançadas para criar visuais detalhados. Filmes de aventura, por sua vez, geralmente incluem cenas de ação extensas, efeitos especiais e gravações em locais variados, o que aumenta os custos de produção.
  2. Gêneros com Orçamento Intermediário:

    • Gêneros como Thriller, War, History, e Mystery têm orçamentos médios na faixa de 30 a 40 milhões de dólares. Estes gêneros tendem a ter menos dependência de efeitos especiais caros, mas ainda requerem orçamentos substanciais para cenários, atores e, em alguns casos, recriação histórica ou cenas de guerra.
  3. Gêneros com Orçamento Baixo:

    • Gêneros como Documentário, e Filmes Estrangeiros (Foreign) estão entre os que têm menores orçamentos médios, com valores abaixo de 20 milhões de dólares. Esses gêneros frequentemente envolvem menos dependência de efeitos visuais e ação, e podem ser produzidos com custos mais baixos, dependendo do enredo e da abordagem de produção.
  4. Diferenças Significativas Entre Gêneros:

    • O gráfico destaca uma clara diferença nos orçamentos médios entre os gêneros mais comerciais e os de nicho. Gêneros como Animação e Aventura demandam grandes investimentos devido às suas produções complexas, enquanto gêneros como Documentário e Filmes Estrangeiros podem ser produzidos de forma mais econômica.
In [42]:
title_corpus = ' '.join(movies_metadata['title'])
overview_corpus = ' '.join(movies_metadata['overview'].astype('str'))

title_wordcloud = WordCloud(stopwords=STOPWORDS, background_color='white', height=2000, width=4000).generate(title_corpus)

overview_corpus_wordcloud = WordCloud(stopwords=STOPWORDS, background_color='white', height=2000, width=4000).generate(overview_corpus)


plt.figure(figsize=(16, 8))
plt.imshow(title_wordcloud, interpolation='bilinear')
plt.axis('off')
plt.title('Nuvem de Palavras - Títulos de Filmes', fontsize=20)
plt.show()


plt.figure(figsize=(16, 8))
plt.imshow(overview_corpus_wordcloud, interpolation='bilinear')
plt.axis('off')
plt.title('Nuvem de Palavras - Sinopse de Filmes', fontsize=20)
plt.show()
No description has been provided for this image
No description has been provided for this image
In [43]:
#Funções para analisar títulos e sinopses

# Função para pré-processar o texto
def preprocess_text(text):
    # Converter para minúsculas
    text = text.lower()
    # Remover pontuação
    text = re.sub(r'[^\w\s]', '', text)
    # Tokenizar
    tokens = word_tokenize(text)
    # Remover stopwords
    stop_words = set(stopwords.words('english'))
    tokens = [word for word in tokens if word not in stop_words]
    return tokens

# Função para obter frequências de palavras
def get_word_frequencies(text_series):
    all_tokens = []
    for text in text_series.dropna():
        tokens = preprocess_text(text)
        all_tokens.extend(tokens)
    word_counts = Counter(all_tokens)
    return word_counts

# Função para plotar nuvem de palavras
def plot_wordcloud(word_counts, title):
    wordcloud = WordCloud(width=800, height=400, background_color='white').generate_from_frequencies(word_counts)
    plt.figure(figsize=(15, 7.5))
    plt.imshow(wordcloud, interpolation='bilinear')
    plt.title(title, fontsize=20)
    plt.axis('off')
    plt.show()

# Função para analisar palavras mais comuns por gênero
def plot_common_words_by_genre(movies_metadata, column, genre, top_n=10):
    """
    Exibe as palavras mais comuns de títulos ou sinopses para um determinado gênero.
    
    Parâmetros:
    - movies_metadata: DataFrame contendo as informações dos filmes.
    - column: Nome da coluna a ser analisada ('title' ou 'overview').
    - genre: O gênero de interesse.
    - top_n: O número de palavras mais frequentes para exibir.
    """
    # Filtrar os filmes pelo gênero
    genre_movies = movies_metadata[movies_metadata['genres'].apply(lambda x: genre in x)]
    
    # Verificar se há dados suficientes
    if genre_movies.empty:
        print(f"Nenhum dado disponível para o gênero '{genre}' na coluna '{column}'.")
        return
    
    # Obter frequências das palavras
    text_series = genre_movies[column].dropna()
    word_counts = get_word_frequencies(text_series)
    
    # Selecionar as top_n palavras mais comuns
    common_words = word_counts.most_common(top_n)
    
    if len(common_words) == 0:
        print(f"Nenhuma palavra encontrada para o gênero '{genre}' na coluna '{column}'.")
        return
    
    # Separar palavras e frequências
    words, counts = zip(*common_words)
    
    # Criar o gráfico
    plt.figure(figsize=(10, 6))
    plt.bar(words, counts, color='skyblue')
    plt.title(f'Palavras Mais Comuns no "{column}" para o Gênero "{genre}"')
    plt.xlabel('Palavras')
    plt.ylabel('Frequência')
    plt.xticks(rotation=45)
    plt.show()

# Função para analisar palavras mais comuns por década
def plot_common_words_by_decade(movies_metadata, column, decade, top_n=10):
    """
    Exibe as palavras mais comuns de títulos ou sinopses para uma determinada década.
    
    Parâmetros:
    - movies_metadata: DataFrame contendo as informações dos filmes.
    - column: Nome da coluna a ser analisada ('title' ou 'overview').
    - decade: A década de interesse (ex: 1990 para a década de 1990-1999).
    - top_n: O número de palavras mais frequentes para exibir.
    """
    # Filtrar os filmes pela década
    decade_movies = movies_metadata[movies_metadata['decade'] == decade]
    
    # Verificar se há dados suficientes
    if decade_movies.empty:
        print(f"Nenhum dado disponível para a década '{decade}' na coluna '{column}'.")
        return
    
    # Obter frequências das palavras
    text_series = decade_movies[column].dropna()
    word_counts = get_word_frequencies(text_series)
    
    # Selecionar as top_n palavras mais comuns
    common_words = word_counts.most_common(top_n)
    
    if len(common_words) == 0:
        print(f"Nenhuma palavra encontrada para a década '{decade}' na coluna '{column}'.")
        return
    
    # Separar palavras e frequências
    words, counts = zip(*common_words)
    
    # Criar o gráfico
    plt.figure(figsize=(10, 6))
    plt.bar(words, counts, color='salmon')
    plt.title(f'Palavras Mais Comuns no "{column}" para a Década de {decade}')
    plt.xlabel('Palavras')
    plt.ylabel('Frequência')
    plt.xticks(rotation=45)
    plt.show()

# Função para obter frequências de n-gramas
def get_ngrams_frequencies(text_series, n=2):
    all_ngrams = []
    for text in text_series.dropna():
        tokens = preprocess_text(text)
        n_grams = ngrams(tokens, n)
        all_ngrams.extend(n_grams)
    ngram_counts = Counter(all_ngrams)
    return ngram_counts

# Função para plotar n-gramas
def plot_ngrams(ngram_counts, title, top_n=10):
    common_ngrams = ngram_counts.most_common(top_n)
    if len(common_ngrams) == 0:
        print("Nenhum n-grama encontrado.")
        return
    ngrams_texts = [' '.join(ngram) for ngram, count in common_ngrams]
    counts = [count for ngram, count in common_ngrams]
    plt.figure(figsize=(10, 6))
    plt.bar(ngrams_texts, counts, color='purple')
    plt.title(title)
    plt.xlabel('N-gramas')
    plt.ylabel('Frequência')
    plt.xticks(rotation=45)
    plt.show()

# Função para obter pontuações de sentimento
def get_sentiment_scores(text_series):
    sentiments = text_series.dropna().apply(lambda x: TextBlob(x).sentiment.polarity)
    return sentiments

# Função para plotar sentimento por gênero
def plot_sentiment_by_genre(movies_metadata):
    genres = set([genre for sublist in movies_metadata['genres'] for genre in sublist])
    avg_sentiments = {}
    for genre in genres:
        genre_movies = movies_metadata[movies_metadata['genres'].apply(lambda x: genre in x)]
        sentiments = get_sentiment_scores(genre_movies['overview'])
        if len(sentiments) > 0:
            avg_sentiments[genre] = sentiments.mean()
    # Plotar
    genres = list(avg_sentiments.keys())
    sentiments = list(avg_sentiments.values())
    plt.figure(figsize=(12,6))
    plt.bar(genres, sentiments, color='orange')
    plt.title('Sentimento Médio por Gênero')
    plt.xlabel('Gênero')
    plt.ylabel('Sentimento Médio')
    plt.xticks(rotation=45)
    plt.show()

# Função para plotar sentimento por década
def plot_sentiment_by_decade(movies_metadata):
    decades = movies_metadata['decade'].dropna().unique()
    avg_sentiments = {}
    for decade in decades:
        decade_movies = movies_metadata[movies_metadata['decade'] == decade]
        sentiments = get_sentiment_scores(decade_movies['overview'])
        if len(sentiments) > 0:
            avg_sentiments[decade] = sentiments.mean()
    # Ordenar por década
    decades = sorted(avg_sentiments.keys())
    sentiments = [avg_sentiments[decade] for decade in decades]
    plt.figure(figsize=(12,6))
    plt.plot(decades, sentiments, marker='o', linestyle='-')
    plt.title('Sentimento Médio por Década')
    plt.xlabel('Década')
    plt.ylabel('Sentimento Médio')
    plt.xticks(decades, rotation=45)
    plt.grid(True)
    plt.show()
In [44]:
# Análise de sentimento nas sinopses
sentiment_scores = get_sentiment_scores(movies_metadata['overview'])
plt.figure(figsize=(10,6))
plt.hist(sentiment_scores, bins=50, color='green')
plt.title('Distribuição dos Sentimentos nas Sinopses')
plt.xlabel('Polaridade')
plt.ylabel('Frequência')
plt.show()

# Análise de sentimento por gênero
plot_sentiment_by_genre(movies_metadata)

# Análise de sentimento por década
plot_sentiment_by_decade(movies_metadata)
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image

Distribuição dos Sentimentos nas Sinopses¶

O gráfico acima exibe a distribuição dos sentimentos nas sinopses dos filmes, com polaridades variando entre -1 (sentimento extremamente negativo) e 1 (sentimento extremamente positivo).

Principais Insights:¶

  1. Pico em Zero: A maior concentração de sinopses está em torno de 0.0, indicando que muitas das sinopses possuem um tom neutro. Esse resultado é esperado, uma vez que muitas descrições de filmes tendem a ser informativas e evitar emoções explícitas.

  2. Tendência Levemente Positiva: Observa-se uma leve inclinação para o lado positivo da polaridade, com um número significativo de sinopses entre 0.0 e 0.3, o que sugere que, embora neutras, muitas sinopses carregam uma leve inclinação positiva.

  3. Distribuição Simétrica: A distribuição se assemelha a uma curva simétrica em torno de zero, com uma pequena quantidade de sinopses fortemente negativas (à esquerda) e fortemente positivas (à direita), sugerindo que as emoções extremas são menos comuns nas sinopses.

Essa análise revela que a maioria das sinopses se mantém dentro de um intervalo neutro a levemente positivo, com poucas variações para os extremos emocionais. Isso pode ocorrer, talvez, por conta de na sinopse existir uma tendência de nâo revelar as principais informações do filme.


Análise do Gráfico de Sentimento Médio por Gênero¶

O gráfico de sentimento médio por gênero revela as diferenças nas emoções retratadas nas sinopses de filmes de diferentes gêneros.

Principais Insights:¶

Gêneros mais positivos: Gêneros como Animação e Família apresentam os sentimentos mais positivos, refletindo histórias leves e voltadas para o público jovem. Gêneros mais negativos: Horror e Mistério mostram os sentimentos mais negativos, o que é esperado devido às temáticas sombrias e emocionais que exploram.


Análise do Gráfico de Sentimento Médio por Década¶

O gráfico de sentimento médio por década mostra como as emoções nas sinopses dos filmes mudaram ao longo do tempo, e parecem ter uma tendência a títulos menos

In [45]:
movies_metadata.columns
Out[45]:
Index(['belongs_to_collection', 'budget', 'genres', 'homepage', 'id',
       'original_language', 'overview', 'popularity', 'production_companies',
       'production_countries', 'release_date', 'revenue', 'runtime',
       'spoken_languages', 'status', 'tagline', 'title', 'video',
       'vote_average', 'vote_count', 'year', 'decade', 'ROI', 'profit',
       'movieId', 'imdbId', 'tmdbId', 'countries', 'release_month', 'is_flop'],
      dtype='object')
In [46]:
# Contagem de filmes que pertencem a uma franquia
franchise_count = movies_metadata['belongs_to_collection'].notnull().sum()
no_franchise_count = movies_metadata['belongs_to_collection'].isnull().sum()

print(f"Filmes que pertencem a uma franquia: {franchise_count}")
print(f"Filmes que não pertencem a uma franquia: {no_franchise_count}")

# Visualizando a distribuição
franchise_data = pd.DataFrame({
    'Tipo de Filme': ['Franquia', 'Independente'],
    'Quantidade': [franchise_count, no_franchise_count]
})

fig = px.bar(franchise_data, x='Tipo de Filme', y='Quantidade',
             title='Quantidade de Filmes que Pertencem a uma Franquia vs Independentes',
             color='Tipo de Filme', color_discrete_sequence=['#FF5733', '#33FF57'])

fig.show()
Filmes que pertencem a uma franquia: 1030
Filmes que não pertencem a uma franquia: 2816
In [47]:
# Comparação de orçamento entre filmes de franquia e independentes
franchise_movies = movies_metadata[movies_metadata['belongs_to_collection'].notnull()]
independent_movies = movies_metadata[movies_metadata['belongs_to_collection'].isnull()]

franchise_avg_budget = franchise_movies['budget'].mean()
independent_avg_budget = independent_movies['budget'].mean()

print(f"Orçamento médio - Filmes de Franquia: {franchise_avg_budget}")
print(f"Orçamento médio - Filmes Independentes: {independent_avg_budget}")

# Visualizando a comparação
budget_data = pd.DataFrame({
    'Tipo de Filme': ['Franquia', 'Independente'],
    'Orçamento Médio (USD)': [franchise_avg_budget, independent_avg_budget]
})

fig = px.bar(budget_data, x='Tipo de Filme', y='Orçamento Médio (USD)',
             title='Orçamento Médio: Filmes de Franquia vs Independentes',
             color='Tipo de Filme', color_discrete_sequence=['#FF5733', '#33FF57'])

fig.show()
Orçamento médio - Filmes de Franquia: 50004512.79805825
Orçamento médio - Filmes Independentes: 31842465.997514203

Orçamento Médio: Filmes de Franquia vs Independentes¶

O gráfico acima compara o orçamento médio entre filmes de franquia e filmes independentes, revelando uma diferença significativa nos valores investidos.

Principais Insights:¶

  1. Filmes de Franquia:

    • O orçamento médio dos filmes de franquia é de aproximadamente 50 milhões de dólares, um valor consideravelmente maior em comparação com os filmes independentes.
    • A hipótese para isso pode ser que filmes que têm um bom retorno financeiro são mais propensos a se tornarem franquias. Assim, os estúdios investem mais em continuações, com base no sucesso inicial.
  2. Filmes Independentes:

    • Por outro lado, filmes independentes possuem um orçamento médio em torno de 31.8 milhões de dólares.
    • Mesmo que alguns filmes independentes tenham um ótimo desempenho, eles geralmente começam com orçamentos menores, devido à incerteza sobre seu sucesso comercial.
  3. Diferença de Orçamento:

    • A diferença observada de cerca de 18 milhões de dólares entre franquias e filmes independentes pode estar relacionado a ser mais garantido o retorno em filmes que sâo franquias, pois os mesmos já são filmes consolidados, e por isso se tornaram franquias.
In [48]:
# Comparação de receita entre filmes de franquia e independentes
franchise_avg_revenue = franchise_movies['revenue'].mean()
independent_avg_revenue = independent_movies['revenue'].mean()

print(f"Receita média - Filmes de Franquia: {franchise_avg_revenue}")
print(f"Receita média - Filmes Independentes: {independent_avg_revenue}")

# Visualizando a comparação
revenue_data = pd.DataFrame({
    'Tipo de Filme': ['Franquia', 'Independente'],
    'Receita Média (USD)': [franchise_avg_revenue, independent_avg_revenue]
})

fig = px.bar(revenue_data, x='Tipo de Filme', y='Receita Média (USD)',
             title='Receita Média: Filmes de Franquia vs Independentes',
             color='Tipo de Filme', color_discrete_sequence=['#FF5733', '#33FF57'])

fig.show()
Receita média - Filmes de Franquia: 212657195.83106795
Receita média - Filmes Independentes: 75103069.56676136

Receita Média: Filmes de Franquia vs Independentes¶

O gráfico compara a receita média entre filmes de franquia e filmes independentes, mostrando uma clara disparidade entre os dois.

Principais Insights:¶

  1. Receita dos Filmes de Franquia:

    • Os filmes de franquia apresentam uma receita média superior a 200 milhões de dólares, um valor consideravelmente maior do que o dos filmes independentes.
    • Esse resultado apoia a hipótese de que filmes que obtêm grande retorno financeiro tendem a se tornar franquias. Ou seja, filmes que têm um bom desempenho nas bilheterias são frequentemente seguidos por sequências, o que resulta em franquias com receitas ainda maiores ao longo do tempo.
  2. Receita dos Filmes Independentes:

    • Os filmes independentes, por outro lado, têm uma receita média mais modesta, em torno de 80 milhões de dólares.
    • Embora alguns filmes independentes possam atingir grande sucesso, a receita tende a ser menor, já que eles não possuem o mesmo apelo comercial, grandes campanhas de marketing ou a força de uma marca consolidada que as franquias muitas vezes têm.
  3. Diferença de Receita:

    • A diferença substancial na receita média entre filmes de franquia e filmes independentes pode não necessariamente significar que franquias geram mais retorno por si mesmas, mas sim que filmes de sucesso tendem a se transformar em franquias, ampliando seus ganhos ao longo do tempo com novas sequências.
In [49]:
# Comparando a média das notas (vote_average)
franchise_avg_rating = franchise_movies['vote_average'].mean()
independent_avg_rating = independent_movies['vote_average'].mean()

print(f"Média de Notas - Filmes de Franquia: {franchise_avg_rating}")
print(f"Média de Notas - Filmes Independentes: {independent_avg_rating}")

# Visualizando a comparação
rating_data = pd.DataFrame({
    'Tipo de Filme': ['Franquia', 'Independente'],
    'Média de Notas': [franchise_avg_rating, independent_avg_rating]
})

fig = px.bar(rating_data, x='Tipo de Filme', y='Média de Notas',
             title='Média de Notas: Filmes de Franquia vs Independentes',
             color='Tipo de Filme', color_discrete_sequence=['#FF5733', '#33FF57'])

fig.show()
Média de Notas - Filmes de Franquia: 6.27621359223301
Média de Notas - Filmes Independentes: 6.446306818181818

Média de Notas: Filmes de Franquia vs Independentes¶

O gráfico compara a média de notas entre filmes de franquia e filmes independentes, revelando que ambos os tipos de filmes apresentam praticamente a mesma média.

Uma hipótese para essa semelhança é que os primeiros filmes de franquias costumam ser bem recebidos pelo público, mas isso não necessáriamente pe verdade para suas continuações. Seria necessário uma análise mais aprofundada para confirmar essa hipótese, com dados que infelizmente não estão contidos no dataset.

In [50]:
ratings = pd.read_csv('./the-movies-dataset/ratings_small.csv')
In [51]:
fig = px.histogram(ratings, x='rating', nbins=20, title='Distribuição das Notas dos Filmes')
fig.update_layout(xaxis_title='Nota', yaxis_title='Frequência')
fig.show()

Distribuição das Notas dos Filmes¶

O gráfico acima mostra a distribuição das notas atribuídas pelos usuários aos filmes, variando de 1 a 5. Um aspecto interessante observado na distribuição é a frequência de notas inteiras versus notas quebradas.

Principais Insights:¶

  1. Notas Inteiras são mais Frequentes:

    • Um ponto que se destaca é que notas inteiras (como 1, 2, 3, 4 e 5) são significativamente mais frequentes do que notas quebradas (como 1.5, 2.5, 3.5, etc.). Isso pode indicar que os usuários preferem usar valores inteiros ao avaliar filmes, possivelmente porque as notas inteiras são mais intuitivas e fáceis de escolher em uma escala de 1 a 5.
  2. Ponto de Transição:

    • A nota 4 também representa um ponto de transição interessante: à medida que subimos na escala de notas, as notas vão crescendo em frequência até atingir o pico na nota 4. Ou seja, a nota 4 é o ponto em que os filmes recebem avaliações mais positivas, sendo a última nota a crescer em frequência antes de uma leve queda na nota 5.
  3. Menor Frequência de Notas Baixas:

    • Notas abaixo de 3 aparecem com muito menos frequência, sugerindo que filmes com avaliações extremamente negativas são menos comuns. Isso pode refletir tanto a qualidade das produções quanto o fato de que os usuários tendem a não assistir ou avaliar filmes que eles acreditam que não irão gostar.
In [52]:
all_users = ratings['userId'].value_counts().reset_index()
all_users.columns = ['userId', 'count']

fig = px.bar(all_users, x='userId', y='count', title='Número de Avaliações por Usuário')
fig.update_layout(xaxis_title='ID do Usuário', yaxis_title='Quantidade de Avaliações')
fig.show()

top_users = ratings['userId'].value_counts().reset_index().head(10)
top_users.columns = ['userId', 'count']

fig = px.bar(top_users, x='userId', y='count', title='Top 10 Usuários que Mais Avaliaram Filmes')
fig.update_layout(xaxis_title='ID do Usuário', yaxis_title='Quantidade de Avaliações')
fig.show()


bottom_users = ratings['userId'].value_counts().reset_index().tail(10)
bottom_users.columns = ['userId', 'count']

fig = px.bar(bottom_users, x='userId', y='count', title='Bottom 10 Usuários que Mais Avaliaram Filmes')
fig.update_layout(xaxis_title='ID do Usuário', yaxis_title='Quantidade de Avaliações')
fig.show()

Todos os usuários avaliaram pelo menos 20 filmes¶

In [53]:
# Filtrar notas acima de 3.5 para maior relevância
filtered_ratings = ratings[ratings['rating'] > 3.5]

# Criar matriz de transações com filmes avaliados
transactions = (filtered_ratings.groupby(['userId', 'movieId'])['rating']
                .max().unstack().reset_index().fillna(0)
                .set_index('userId'))

# Aplicar uma função binária, mantendo filmes com avaliação alta (>= 3.5)
transactions = transactions.applymap(lambda x: 1 if x > 3.5 else 0).astype(bool)

# Verificando as transações
print(f"Total de transações: {len(transactions)}")
print(f"Total de itens (filmes): {transactions.shape[1]}")
print(f"Quantidade de 1s na matriz de transações: {transactions.sum().sum()}")

# Ajustando min_support para gerar mais conjuntos frequentes
with tqdm(total=len(transactions.columns), desc="Calculando conjuntos frequentes") as pbar_frequent:
    frequent_itemsets = apriori(transactions, min_support=0.1, use_colnames=True, low_memory=True)  # Reduzido de 0.15 para 0.1
    pbar_frequent.update(len(transactions.columns))

# Verificando o número de conjuntos frequentes encontrados
print(f"Total de conjuntos frequentes encontrados: {len(frequent_itemsets)}")
if frequent_itemsets.empty:
    print("Nenhum conjunto frequente encontrado. Considere diminuir o min_support ou ajustar os dados.")
else:
    # Calculando regras de associação com um min_lift mais baixo (ex.: 1.1) e min_confidence mais baixo (ex.: 0.5)
    with tqdm(total=len(frequent_itemsets), desc="Calculando regras de associação") as pbar_rules:
        rules = association_rules(frequent_itemsets, metric="lift", min_threshold=1.1)  # Reduzido de 1.2 para 1.1
        pbar_rules.update(len(frequent_itemsets))
    
    # Verificando se há regras de associação
    if not rules.empty:
        print(rules[['antecedents', 'consequents', 'support', 'confidence', 'lift']])
    else:
        print("Nenhuma regra encontrada.")
C:\Users\Eric\AppData\Local\Temp\ipykernel_22732\3131926791.py:10: FutureWarning:

DataFrame.applymap has been deprecated. Use DataFrame.map instead.

Total de transações: 671
Total de itens (filmes): 6170
Quantidade de 1s na matriz de transações: 51568
Calculando conjuntos frequentes: 100%|██████████| 6170/6170 [00:01<00:00, 3707.81it/s]
Total de conjuntos frequentes encontrados: 533
Calculando regras de associação: 100%|██████████| 533/533 [00:00<00:00, 14934.23it/s]
       antecedents         consequents   support  confidence      lift
0              (1)               (260)  0.119225    0.503145  1.455216
1            (260)                 (1)  0.119225    0.344828  1.455216
2            (296)                 (1)  0.117735    0.313492  1.322976
3              (1)               (296)  0.117735    0.496855  1.322976
4              (1)               (318)  0.129657    0.547170  1.339967
...            ...                 ...       ...         ...       ...
1555  (7153, 2571)        (5952, 4993)  0.117735    0.868132  4.623147
1556        (5952)  (4993, 2571, 7153)  0.117735    0.556338  4.497624
1557        (4993)  (5952, 7153, 2571)  0.117735    0.496855  3.922235
1558        (2571)  (5952, 4993, 7153)  0.117735    0.403061  2.436523
1559        (7153)  (5952, 4993, 2571)  0.117735    0.598485  4.462037

[1560 rows x 5 columns]

Regras de Associação sobre Filmes Avaliados¶

A análise de regras de associação aplicada aos dados de filmes avaliados com nota igual ou superior a 3 gerou alguns insights importantes:

  1. Filmes Combinados Frequentes:

    • Os conjuntos frequentes são combinações de filmes que foram avaliados positivamente juntos por uma parte significativa dos usuários. Isso pode indicar tendências de preferência entre os usuários, sugerindo que determinados filmes são frequentemente vistos e bem avaliados em conjunto.
    • Por exemplo, se um usuário avaliou positivamente o filme A, é bastante provável que também tenha avaliado positivamente o filme B.
  2. Confiabilidade das Regras:

    • A métrica de confidence nas regras geradas indica a probabilidade de um filme ser bem avaliado se outro filme já foi bem avaliado. Regras com alta confiança sugerem uma relação forte entre os filmes avaliados positivamente.
    • Se a confiança para um par de filmes for, por exemplo, 0.60, isso indica que em 60% dos casos, quando o filme A é avaliado positivamente, o filme B também o é.
  3. Elevação (Lift):

    • O lift é outra métrica importante que mede o quão maior é a probabilidade de os filmes serem avaliados juntos em relação à probabilidade de serem avaliados individualmente. Um lift acima de 1 indica uma correlação positiva, sugerindo que os filmes estão relacionados em termos de preferência do usuário.
    • Um lift de 3, por exemplo, significa que os filmes são 3 vezes mais propensos a serem avaliados juntos do que individualmente.

Esses insights podem ser utilizados para:

  • Recomendações personalizadas: Se um usuário já avaliou positivamente um filme de um conjunto frequente, é possível recomendar os outros filmes do mesmo conjunto.
  • Identificar padrões de preferência: Os dados revelam padrões sobre os gêneros ou tipos de filmes que são mais frequentemente bem avaliados juntos.
In [54]:
# Função para substituir IDs por títulos de filmes
def substituir_ids_por_titulos(ids, movies_metadata):
    filmes = [movies_metadata[movies_metadata['movieId'] == i]['title'].values[0] for i in ids]
    return filmes

# Filtrar regras com alta confiança e lift
high_confidence_rules = rules[(rules['confidence'] > 0.7) & (rules['lift'] > 1.5)]

# Mostrar regras mais fortes ordenadas por lift
strongest_rules = high_confidence_rules.sort_values(by='lift', ascending=False).head(10)

# Substituir IDs dos filmes por títulos nas colunas 'antecedents' e 'consequents'
strongest_rules['antecedents'] = strongest_rules['antecedents'].apply(lambda x: substituir_ids_por_titulos(list(x), movies_metadata))
strongest_rules['consequents'] = strongest_rules['consequents'].apply(lambda x: substituir_ids_por_titulos(list(x), movies_metadata))

print("Regras de associação mais fortes (com títulos):")
print(strongest_rules[['antecedents', 'consequents', 'support', 'confidence', 'lift']])

# Verificar filmes mais frequentemente avaliados juntos
most_frequent_sets = frequent_itemsets.sort_values(by='support', ascending=False).head(10)

# Substituir IDs por títulos no conjunto frequente
most_frequent_sets['itemsets'] = most_frequent_sets['itemsets'].apply(lambda x: substituir_ids_por_titulos(list(x), movies_metadata))

print("\nConjuntos frequentes mais comuns (com títulos):")
print(most_frequent_sets)

# Resumo geral dos resultados
print(f"Total de transações: {len(transactions)}")
print(f"Total de conjuntos frequentes encontrados: {len(frequent_itemsets)}")
print(f"Total de regras de associação geradas: {len(rules)}")
Regras de associação mais fortes (com títulos):
                                            antecedents  \
1497  [The Lord of the Rings: The Fellowship of the ...   
1496  [The Lord of the Rings: The Two Towers, Star W...   
1480  [The Lord of the Rings: The Fellowship of the ...   
1485  [The Lord of the Rings: The Return of the King...   
1484  [The Lord of the Rings: The Return of the King...   
1468  [The Lord of the Rings: The Two Towers, Star W...   
1469  [The Lord of the Rings: The Fellowship of the ...   
1467  [The Lord of the Rings: The Two Towers, The Em...   
1470  [The Lord of the Rings: The Fellowship of the ...   
1495  [The Lord of the Rings: The Two Towers, Return...   

                                            consequents   support  confidence  \
1497  [The Lord of the Rings: The Two Towers, Star W...  0.101341    0.839506   
1496  [The Lord of the Rings: The Fellowship of the ...  0.101341    0.772727   
1480  [The Lord of the Rings: The Return of the King...  0.105812    0.747368   
1485  [The Lord of the Rings: The Fellowship of the ...  0.105812    0.865854   
1484  [The Lord of the Rings: The Fellowship of the ...  0.105812    0.922078   
1468  [The Lord of the Rings: The Fellowship of the ...  0.110283    0.840909   
1469  [The Lord of the Rings: The Two Towers, Star W...  0.110283    0.778947   
1467  [The Lord of the Rings: The Fellowship of the ...  0.110283    0.902439   
1470  [The Lord of the Rings: The Two Towers, The Em...  0.110283    0.718447   
1495  [The Lord of the Rings: The Fellowship of the ...  0.101341    0.894737   

          lift  
1497  6.401235  
1496  6.401235  
1480  6.115661  
1485  6.115661  
1484  6.006935  
1468  5.939474  
1469  5.939474  
1467  5.878996  
1470  5.878996  
1495  5.828820  

Conjuntos frequentes mais comuns (com títulos):
     support                    itemsets
13  0.408346  [The Shawshank Redemption]
12  0.375559              [Pulp Fiction]
15  0.374069              [Forrest Gump]
28  0.356185  [The Silence of the Lambs]
10  0.345753                 [Star Wars]
22  0.307004          [Schindler's List]
81  0.292101                [The Matrix]
49  0.281669   [The Empire Strikes Back]
30  0.274218                     [Fargo]
84  0.268256           [American Beauty]
Total de transações: 671
Total de conjuntos frequentes encontrados: 533
Total de regras de associação geradas: 1560
In [55]:
def recomendar_filmes_por_preferencia(usuario, filmes_gostados, transacoes, regras_associacao, n_recomendacoes=3, min_confidence=0.7, min_support=0.3, min_lift=1.2):
    """
    Função para recomendar filmes a um usuário com base em uma lista de filmes que ele gostou e regras de associação,
    considerando confiança, suporte e lift mínimos.

    Parâmetros:
    - usuario: ID do usuário.
    - filmes_gostados: Lista de IDs dos filmes que o usuário gostou.
    - transacoes: DataFrame das transações de filmes.
    - regras_associacao: DataFrame das regras de associação.
    - n_recomendacoes: Número de recomendações desejadas.
    - min_confidence: Confiança mínima das regras para considerar na recomendação.
    - min_support: Suporte mínimo das regras para considerar na recomendação.
    - min_lift: Lift mínimo das regras para considerar na recomendação.

    Retorna:
    - Lista de títulos de filmes recomendados.
    """
    
    recomendacoes = []
    
    # Iterar pelos filmes que o usuário gostou e buscar regras de associação
    for filme in filmes_gostados:
        # Filtrar as regras que têm o filme nos antecedentes e que atendem os critérios de confiança, suporte e lift mínimos
        regras_filme = regras_associacao[
            (regras_associacao['antecedents'].apply(lambda x: filme in x)) &
            (regras_associacao['confidence'] >= min_confidence) &
            (regras_associacao['support'] >= min_support) &
            (regras_associacao['lift'] >= min_lift)
        ]
        
        # Ordenar regras por confiança e lift
        regras_filme = regras_filme.sort_values(by=['confidence', 'lift'], ascending=False)
        
        # Iterar pelas regras e adicionar filmes recomendados
        for _, regra in regras_filme.iterrows():
            recomendados = regra['consequents']
            for filme_recomendado in recomendados:
                if filme_recomendado not in filmes_gostados and filme_recomendado not in recomendacoes:
                    recomendacoes.append(filme_recomendado)
                    
            if len(recomendacoes) >= n_recomendacoes:
                break
        
        if len(recomendacoes) >= n_recomendacoes:
            break
    
    # Verificar se há recomendações e se os filmes existem no metadata
    if recomendacoes:
        recomendacoes_nomes = movies_metadata[movies_metadata['movieId'].isin(recomendacoes)]['title'].tolist()
        
        if not recomendacoes_nomes:
            print(f"Não foi possível encontrar os títulos dos filmes recomendados para o usuário {usuario}. IDs dos filmes recomendados: {recomendacoes}")
        
        return recomendacoes_nomes[:n_recomendacoes]
    else:
        print(f"Nenhuma recomendação foi gerada para o usuário {usuario} com base nos filmes que ele gostou.")
        return []

# Função para selecionar um filme aleatório que o usuário gostou
def selecionar_filme_aleatorio_usuario(filtered_ratings, usuario_id):
    filmes_usuario = filtered_ratings[filtered_ratings['userId'] == usuario_id]
    
    if not filmes_usuario.empty:
        filmes_avaliados = filmes_usuario[filmes_usuario['rating'] > 3]['movieId'].tolist()
        if filmes_avaliados:
            return random.choice(filmes_avaliados)
        else:
            print(f"Usuário {usuario_id} não avaliou filmes com nota superior a 3.")
            return None
    else:
        print(f"Nenhum filme encontrado para o usuário {usuario_id}.")
        return None


def recomendar_filmes(usuario, transacoes, regras_associacao, n_recomendacoes=3, min_confidence=0.7, min_support=0.3, min_lift=1.2):
    """
    Função para recomendar filmes a um usuário com base em regras de associação, considerando confiança, suporte e lift mínimos.

    Parâmetros:
    - usuario: ID do usuário.
    - transacoes: DataFrame das transações de filmes.
    - regras_associacao: DataFrame das regras de associação.
    - n_recomendacoes: Número de recomendações desejadas.
    - min_confidence: Confiança mínima das regras para considerar na recomendação.
    - min_support: Suporte mínimo das regras para considerar na recomendação.
    - min_lift: Lift mínimo das regras para considerar na recomendação.

    Retorna:
    - Lista de títulos de filmes recomendados.
    """
    
    # Obter a lista de filmes assistidos pelo usuário
    filmes_usuario = transacoes.loc[usuario]
    filmes_assistidos = filmes_usuario[filmes_usuario == 1].index.tolist()
    
    recomendacoes = []
    
    # Iterar pelos filmes assistidos e buscar regras de associação
    for filme in filmes_assistidos:
        # Filtrar as regras que têm o filme nos antecedentes e que atendem os critérios de confiança, suporte e lift mínimos
        regras_filme = regras_associacao[
            (regras_associacao['antecedents'].apply(lambda x: filme in x)) &
            (regras_associacao['confidence'] >= min_confidence) &
            (regras_associacao['support'] >= min_support) &
            (regras_associacao['lift'] >= min_lift)
        ]
        
        # Ordenar regras por confiança e lift
        regras_filme = regras_filme.sort_values(by=['confidence', 'lift'], ascending=False)
        
        # Iterar pelas regras e adicionar filmes recomendados
        for _, regra in regras_filme.iterrows():
            recomendados = regra['consequents']
            for filme_recomendado in recomendados:
                if filme_recomendado not in filmes_assistidos and filme_recomendado not in recomendacoes:
                    recomendacoes.append(filme_recomendado)
                    
            if len(recomendacoes) >= n_recomendacoes:
                break
        
        if len(recomendacoes) >= n_recomendacoes:
            break
    
    # Obter os títulos dos filmes recomendados
    recomendacoes_nomes = movies_metadata[movies_metadata['movieId'].isin(recomendacoes)]['title'].tolist()
    
    return recomendacoes_nomes[:n_recomendacoes]
In [56]:
# Selecionando 10 usuários aleatórios
random.seed(42)  # Definindo uma semente para garantir que os resultados sejam reproduzíveis
random_users = filtered_ratings['userId'].drop_duplicates().sample(10).tolist()

for usuario_id in random_users:
    recomendacoes = recomendar_filmes(usuario_id, transactions, rules, n_recomendacoes=3, min_confidence=0.7, min_support=0.1, min_lift=1.2)
    print(f"Filmes recomendados para o usuário {usuario_id}: {recomendacoes}")
Filmes recomendados para o usuário 81: ['Pulp Fiction']
Filmes recomendados para o usuário 154: ['Star Wars', 'Pulp Fiction', 'The Empire Strikes Back']
Filmes recomendados para o usuário 197: ['Raiders of the Lost Ark', 'The Lord of the Rings: The Fellowship of the Ring', 'The Lord of the Rings: The Two Towers']
Filmes recomendados para o usuário 52: ['Pulp Fiction', 'The Silence of the Lambs']
Filmes recomendados para o usuário 174: []
Filmes recomendados para o usuário 266: ['Star Wars', 'Pulp Fiction', 'The Lord of the Rings: The Two Towers']
Filmes recomendados para o usuário 72: ['Star Wars', 'The Empire Strikes Back', 'The Lord of the Rings: The Two Towers']
Filmes recomendados para o usuário 332: ['Pulp Fiction']
Filmes recomendados para o usuário 219: ['The Silence of the Lambs', 'The Empire Strikes Back', 'The Lord of the Rings: The Two Towers']
Filmes recomendados para o usuário 350: ['Pulp Fiction', 'The Lord of the Rings: The Two Towers', 'The Lord of the Rings: The Return of the King']

Explicação do Código de Recomendação de Filmes com Regras de Associação¶

Este código utiliza regras de associação para recomendar filmes para os usuários com base em suas avaliações anteriores. Vamos detalhar as principais etapas do processo e o objetivo final.

1. Filtragem de Avaliações:¶

Primeiro, filtramos as avaliações dos filmes, mantendo apenas aquelas com notas maiores ou iguais a 3. Isso significa que consideramos apenas os filmes que os usuários avaliaram de forma positiva, ou seja, que provavelmente gostaram.

2. Criação da Matriz de Transações:¶

Depois da filtragem, uma matriz de transações é criada, onde:

  • As linhas representam os usuários.
  • As colunas representam os filmes.
  • Se o valor da célula for 1, significa que o usuário avaliou aquele filme positivamente (nota maior ou igual a 3). Caso contrário, o valor será 0. A matriz resultante serve como base para o cálculo das associações entre os filmes.

3. Apriori e Conjuntos Frequentes:¶

Em seguida, utilizamos o algoritmo Apriori para encontrar conjuntos frequentes de filmes. Um conjunto frequente é um grupo de filmes que frequentemente aparecem juntos nas avaliações positivas dos usuários. Por exemplo, se muitos usuários que assistiram a "Filme A" também assistiram a "Filme B", esses dois filmes formam um conjunto frequente.

A métrica min_support foi definida como 0.1, ou seja, consideramos conjuntos frequentes aqueles que aparecem em pelo menos 10% das transações.

4. Regras de Associação:¶

Com os conjuntos frequentes encontrados, o próximo passo é gerar regras de associação usando o algoritmo. As regras de associação ajudam a identificar relações do tipo:

  • "Se o usuário assistiu a Filme A, ele também pode gostar de Filme B."

Essas regras são calculadas com base em métricas como:

  • Suporte: Proporção de transações onde os filmes apareceram juntos.
  • Confiança: A probabilidade de o usuário assistir a Filme B dado que já assistiu a Filme A.
  • Lift: A medida de quão mais provável é que o usuário veja Filme B depois de assistir Filme A, comparado ao acaso.

5. Recomendação de Filmes:¶

A função recomendar_filmes é responsável por gerar recomendações personalizadas para cada usuário. Para isso:

  • Primeiro, verificamos os filmes que o usuário já assistiu.
  • Depois, com base nas regras de associação geradas, recomendamos filmes que ele ainda não assistiu, mas que têm uma alta probabilidade de serem do interesse dele, com base nos filmes que ele já gostou.
In [73]:
usuarios_com_avaliacoes_altas = filtered_ratings['userId'].drop_duplicates()
usuarios_selecionados = usuarios_com_avaliacoes_altas.sample(2, random_state=42).tolist()

# Recomendando filmes para os 2 usuários selecionados
for usuario_id in usuarios_selecionados:
    # Selecionar aleatoriamente um filme que o usuário gostou
    filme_aleatorio = selecionar_filme_aleatorio_usuario(filtered_ratings, usuario_id)
    
    if filme_aleatorio is not None:
        # Obter o título do filme selecionado
        filme_selecionado_titulo = movies_metadata[movies_metadata['movieId'] == filme_aleatorio]['title'].values
        
        if filme_selecionado_titulo.size > 0:
            filme_selecionado_titulo = filme_selecionado_titulo[0]
            
            # Fazer a recomendação para o usuário com base no filme selecionado
            recomendacoes = recomendar_filmes_por_preferencia(usuario_id, [filme_aleatorio], transactions, rules, n_recomendacoes=1, min_confidence=0.5, min_support=0.1, min_lift=1.2)
            
            # Printar o ID do usuário e o filme selecionado
            print(f"Usuário {usuario_id}: Gostou do filme '{filme_selecionado_titulo}' (ID {filme_aleatorio})")
            print(f"Filmes recomendados para o usuário {usuario_id}: {recomendacoes}\n")
        else:
            print(f"Título não encontrado para o filme com ID {filme_aleatorio} do usuário {usuario_id}.")
    else:
        print(f"Não foi possível selecionar um filme para o usuário {usuario_id}.")
Usuário 362: Gostou do filme 'One Flew Over the Cuckoo's Nest' (ID 1193)
Filmes recomendados para o usuário 362: ['The Godfather']

Usuário 159: Gostou do filme 'Blade Runner' (ID 541)
Filmes recomendados para o usuário 159: ['Star Wars']